diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index f1da210e4..a19ef2210 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -54,7 +54,7 @@ import org.mozilla.fenix.library.bookmarks.BookmarkFragmentDirections import org.mozilla.fenix.library.history.HistoryFragmentDirections import org.mozilla.fenix.perf.HotStartPerformanceMonitor import org.mozilla.fenix.search.SearchFragmentDirections -import org.mozilla.fenix.settings.AboutFragmentDirections +import org.mozilla.fenix.settings.about.AboutFragmentDirections import org.mozilla.fenix.settings.DefaultBrowserSettingsFragmentDirections import org.mozilla.fenix.settings.SettingsFragmentDirections import org.mozilla.fenix.settings.TrackingProtectionFragmentDirections diff --git a/app/src/main/java/org/mozilla/fenix/settings/AboutFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/AboutFragment.kt deleted file mode 100644 index 5bfa8aa10..000000000 --- a/app/src/main/java/org/mozilla/fenix/settings/AboutFragment.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.settings - -import android.content.Intent -import android.content.pm.PackageManager -import android.os.Build -import android.os.Build.VERSION.SDK_INT -import android.os.Bundle -import android.view.View -import androidx.annotation.RequiresApi -import androidx.core.content.pm.PackageInfoCompat -import androidx.fragment.app.Fragment -import com.google.android.gms.oss.licenses.OssLicensesMenuActivity -import kotlinx.android.synthetic.main.fragment_about.* -import org.mozilla.fenix.BrowserDirection -import org.mozilla.fenix.BuildConfig -import org.mozilla.fenix.HomeActivity -import org.mozilla.fenix.R -import org.mozilla.fenix.components.metrics.Event -import org.mozilla.fenix.ext.metrics -import org.mozilla.fenix.whatsnew.WhatsNew -import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig - -/** - * Displays the logo and information about the app, including library versions. - */ -class AboutFragment : Fragment(R.layout.fragment_about) { - - /** - * Sets the activity title, displays library version strings, and sets up the [view_licenses_button]. - */ - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - val appName = getString(R.string.app_name) - activity?.title = getString(R.string.preferences_about, appName) - - val aboutText = try { - val packageInfo = requireContext().packageManager.getPackageInfo(requireContext().packageName, 0) - val versionCode = PackageInfoCompat.getLongVersionCode(packageInfo).toString() - val componentsVersion = mozilla.components.Build.version + ", " + mozilla.components.Build.gitHash - val maybeGecko = if (SDK_INT >= Build.VERSION_CODES.N) GECKO_EMOJI else "GV" - val geckoVersion = GeckoViewBuildConfig.MOZ_APP_VERSION + "-" + GeckoViewBuildConfig.MOZ_APP_BUILDID - - String.format( - "%s (Build #%s)\n%s: %s\n%s: %s", - packageInfo.versionName, - versionCode, - COMPONENTS_EMOJI, - componentsVersion, - maybeGecko, - geckoVersion - ) - } catch (e: PackageManager.NameNotFoundException) { - "" - } - - val content = getString(R.string.about_content, appName) - val buildDate = BuildConfig.BUILD_DATE - - about_text.text = aboutText - about_content.text = content - build_date.text = buildDate - - view_licenses_button.setOnClickListener { - startActivity(Intent(context, OssLicensesMenuActivity::class.java)) - OssLicensesMenuActivity.setActivityTitle(getString(R.string.open_source_licenses_title, appName)) - } - - with(whats_new_button) { - text = getString(R.string.about_whats_new, getString(R.string.app_name)) - setOnClickListener { - context.metrics.track(Event.WhatsNewTapped(Event.WhatsNewTapped.Source.ABOUT)) - WhatsNew.userViewedWhatsNew(context!!) - (activity as HomeActivity).openToBrowserAndLoad( - searchTermOrURL = SupportUtils.getSumoURLForTopic( - context!!, - SupportUtils.SumoTopic.WHATS_NEW - ), - newTab = true, - from = BrowserDirection.FromAbout - ) - } - } - } - - companion object { - private const val COMPONENTS_EMOJI = "\uD83D\uDCE6" - @RequiresApi(Build.VERSION_CODES.N) - private const val GECKO_EMOJI = "\uD83E\uDD8E" - } -} diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt new file mode 100644 index 000000000..81f64a8f1 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt @@ -0,0 +1,168 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.about + +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.pm.PackageInfoCompat +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.DividerItemDecoration +import com.google.android.gms.oss.licenses.OssLicensesMenuActivity +import kotlinx.android.synthetic.main.fragment_about.* +import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.mozilla.fenix.BuildConfig +import org.mozilla.fenix.R +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.ext.requireComponents +import org.mozilla.fenix.lib.Do +import org.mozilla.fenix.settings.SupportUtils +import org.mozilla.fenix.settings.about.AboutItemType.LICENSING_INFO +import org.mozilla.fenix.settings.about.AboutItemType.PRIVACY_NOTICE +import org.mozilla.fenix.settings.about.AboutItemType.RIGHTS +import org.mozilla.fenix.settings.about.AboutItemType.SUPPORT +import org.mozilla.fenix.settings.about.AboutItemType.WHATS_NEW +import org.mozilla.fenix.whatsnew.WhatsNew +import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig + +/** + * Displays the logo and information about the app, including library versions. + */ +class AboutFragment : Fragment(), AboutPageListener { + private lateinit var appName: String + private val aboutPageAdapter: AboutPageAdapter = AboutPageAdapter(this) + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val rootView = inflater.inflate(R.layout.fragment_about, container, false) + appName = getString(R.string.app_name) + activity?.title = getString(R.string.preferences_about, appName) + + return rootView + } + + @ExperimentalCoroutinesApi + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + about_list.run { + adapter = aboutPageAdapter + addItemDecoration( + DividerItemDecoration( + context, + DividerItemDecoration.VERTICAL + ) + ) + } + + populateAboutHeader() + aboutPageAdapter.updateData(populateAboutList()) + } + + private fun populateAboutHeader() { + val aboutText = try { + val packageInfo = requireContext().packageManager.getPackageInfo(requireContext().packageName, 0) + val versionCode = PackageInfoCompat.getLongVersionCode(packageInfo).toString() + val componentsVersion = mozilla.components.Build.version + ", " + mozilla.components.Build.gitHash + val maybeGecko = getString(R.string.gecko_view_abbreviation) + val geckoVersion = GeckoViewBuildConfig.MOZ_APP_VERSION + "-" + GeckoViewBuildConfig.MOZ_APP_BUILDID + + String.format( + "%s (Build #%s)\n%s\n%s: %s", + packageInfo.versionName, + versionCode, + componentsVersion, + maybeGecko, + geckoVersion + ) + } catch (e: PackageManager.NameNotFoundException) { + "" + } + + val content = getString(R.string.about_content, appName) + val buildDate = BuildConfig.BUILD_DATE + + about_text.text = aboutText + about_content.text = content + build_date.text = buildDate + } + + private fun populateAboutList(): List { + val context = requireContext() + + return listOf( + AboutPageItem.Item( + AboutItem.ExternalLink( + WHATS_NEW, + SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.WHATS_NEW) + ), getString(R.string.about_whats_new, getString(R.string.app_name)) + ), + AboutPageItem.Item( + AboutItem.ExternalLink( + SUPPORT, + SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.HELP) + ), getString(R.string.about_support) + ), + AboutPageItem.Item( + AboutItem.ExternalLink( + PRIVACY_NOTICE, + SupportUtils.getPrivacyNoticeUrl() + ), getString(R.string.about_privacy_notice) + ), + AboutPageItem.Item( + AboutItem.ExternalLink( + RIGHTS, + SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.YOUR_RIGHTS) + ), getString(R.string.about_know_your_rights) + ), + AboutPageItem.Item( + AboutItem.ExternalLink(LICENSING_INFO, ABOUT_LICENSE_URL), + getString(R.string.about_licensing_information) + ), + AboutPageItem.Item( + AboutItem.Libraries, + getString(R.string.about_other_open_source_libraries) + ) + ) + } + + private fun openLinkInCustomTab(url: String) { + context?.let { context -> + val intent = SupportUtils.createCustomTabIntent(context, url) + startActivity(intent) + } + } + + private fun openLibrariesPage() { + startActivity(Intent(context, OssLicensesMenuActivity::class.java)) + OssLicensesMenuActivity.setActivityTitle(getString(R.string.open_source_licenses_title, appName)) + } + + override fun onAboutItemClicked(item: AboutItem) { + Do exhaustive when (item) { + is AboutItem.ExternalLink -> { + if (item.type == WHATS_NEW) { + WhatsNew.userViewedWhatsNew(requireContext()) + requireComponents.analytics.metrics.track(Event.WhatsNewTapped(Event.WhatsNewTapped.Source.ABOUT)) + } + + openLinkInCustomTab(item.url) + } + is AboutItem.Libraries -> { + openLibrariesPage() + } + } + } + + companion object { + private const val ABOUT_LICENSE_URL = "about:license" + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/AboutItem.kt b/app/src/main/java/org/mozilla/fenix/settings/about/AboutItem.kt new file mode 100644 index 000000000..f2df25a1c --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/about/AboutItem.kt @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.about + +sealed class AboutItem { + data class ExternalLink(val type: AboutItemType, val url: String) : AboutItem() + object Libraries : AboutItem() +} + +enum class AboutItemType { + WHATS_NEW, SUPPORT, PRIVACY_NOTICE, RIGHTS, LICENSING_INFO +} + +sealed class AboutPageItem { + data class Item(val type: AboutItem, val title: String) : AboutPageItem() +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/AboutPageAdapter.kt b/app/src/main/java/org/mozilla/fenix/settings/about/AboutPageAdapter.kt new file mode 100644 index 000000000..03e2737fb --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/about/AboutPageAdapter.kt @@ -0,0 +1,39 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.about + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import org.mozilla.fenix.settings.about.viewholders.AboutItemViewHolder + +class AboutPageAdapter(private val listener: AboutPageListener) : RecyclerView.Adapter() { + + private var aboutList: List? = null + + fun updateData(items: List) { + this.aboutList = items + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AboutItemViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(AboutItemViewHolder.LAYOUT_ID, parent, false) + + return AboutItemViewHolder(view, listener) + } + + override fun getItemCount(): Int = aboutList?.size ?: 0 + + override fun onBindViewHolder(holder: AboutItemViewHolder, position: Int) { + (aboutList?.get(position) as AboutPageItem.Item).also { + holder.bind(it) + } + } +} + +interface AboutPageListener { + fun onAboutItemClicked(item: AboutItem) +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/viewholders/AboutItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/settings/about/viewholders/AboutItemViewHolder.kt new file mode 100644 index 000000000..64c6ff87f --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/about/viewholders/AboutItemViewHolder.kt @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.about.viewholders + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.about_list_item.view.* +import org.mozilla.fenix.R +import org.mozilla.fenix.settings.about.AboutPageItem +import org.mozilla.fenix.settings.about.AboutPageListener + +class AboutItemViewHolder( + view: View, + listener: AboutPageListener +) : RecyclerView.ViewHolder(view) { + + private val title = view.about_item_title + private lateinit var item: AboutPageItem.Item + + init { + itemView.setOnClickListener { + listener.onAboutItemClicked(item.type) + } + } + + fun bind(item: AboutPageItem.Item) { + this.item = item + title.text = item.title + } + + companion object { + const val LAYOUT_ID = R.layout.about_list_item + } +} diff --git a/app/src/main/res/layout/about_list_item.xml b/app/src/main/res/layout/about_list_item.xml new file mode 100644 index 000000000..c27d7d3bb --- /dev/null +++ b/app/src/main/res/layout/about_list_item.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml index 9f803d14e..6830fccd9 100644 --- a/app/src/main/res/layout/fragment_about.xml +++ b/app/src/main/res/layout/fragment_about.xml @@ -2,100 +2,111 @@ - + + + android:layout_width="match_parent" + android:layout_height="wrap_content"> + android:id="@+id/wordmark" + android:layout_width="0dp" + android:layout_height="@dimen/about_header_fenix_logo_height" + android:layout_marginStart="@dimen/about_header_icon_margin_start_end" + android:layout_marginTop="@dimen/about_header_icon_margin_top" + android:layout_marginEnd="@dimen/about_header_icon_margin_start_end" + android:contentDescription="@string/app_name" + android:importantForAccessibility="no" + android:src="?fenixLogo" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_percent="0.75" /> - - - - - - - - + app:layout_constraintTop_toBottomOf="@id/wordmark" + app:layout_constraintWidth_percent="0.8" + tools:text="@string/about_content" /> + + + + + + + + + + - + diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 01088dab8..a31013539 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -456,7 +456,7 @@ @color/toolbar_divider_color_dark_theme @color/accent_on_dark_background_normal_theme @color/primary_text_normal_theme + @color/about_content_text_dark_theme diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 66bec6321..9635ed14c 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -33,6 +33,7 @@ @color/foundation_light_theme #CDCCCF @color/accent_light_theme + #232749 #FBFBFE @@ -62,6 +63,7 @@ @color/foundation_dark_theme @color/neutral_faded_dark_theme @color/accent_on_dark_background_normal_theme + #FBFBFE #FBFBFE @@ -117,7 +119,7 @@ @color/toolbar_divider_color_light_theme @color/fill_link_from_clipboard_light_theme @color/accent_light_theme - + @color/about_content_text_light_theme #DFDFE3 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 355a9afc8..34cc1a56c 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -79,6 +79,18 @@ 12sp 15sp - + + 56dp + 8dp + 80dp + 36dp + 84dp + 16dp + 84dp + 24dp + 24dp + 36dp + 16sp + 4dp diff --git a/app/src/main/res/values/static_strings.xml b/app/src/main/res/values/static_strings.xml index c94b703e8..826c9029d 100644 --- a/app/src/main/res/values/static_strings.xml +++ b/app/src/main/res/values/static_strings.xml @@ -24,4 +24,7 @@ + + + GV diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 658f9202a..23df7836c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -941,6 +941,17 @@ The first parameter is the app name --> %s | OSS Libraries + + Support + + Privacy notice + + Know your rights + + Licensing information + + Libraries that we use + 1 tab diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index c67722961..6a46448ef 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -355,7 +355,24 @@ + + + + diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index f96fbd6a3..35a1f3242 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -88,9 +88,6 @@ android:icon="@drawable/ic_data_collection" android:key="@string/pref_key_data_choices" android:title="@string/preferences_data_collection" /> - -