From b84f55c9800ee0db82238ea38055078b30bba3ef Mon Sep 17 00:00:00 2001 From: Kainalu Hagiwara Date: Thu, 25 Jun 2020 08:25:16 -0700 Subject: [PATCH 01/17] For #11677 - Adjust tab counter menu item order. --- .../toolbar/TabCounterToolbarButton.kt | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterToolbarButton.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterToolbarButton.kt index 36baffd60..f8739c2ad 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterToolbarButton.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterToolbarButton.kt @@ -19,6 +19,7 @@ import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.sessionsOfType +import org.mozilla.fenix.ext.settings import org.mozilla.fenix.theme.ThemeManager import java.lang.ref.WeakReference @@ -81,16 +82,6 @@ class TabCounterToolbarButton( val primaryTextColor = ThemeManager.resolveAttribute(R.attr.primaryText, context) val metrics = context.components.analytics.metrics val menuItems = listOf( - BrowserMenuImageText( - label = context.getString(R.string.close_tab), - imageResource = R.drawable.ic_close, - iconTintColorResource = primaryTextColor, - textColorResource = primaryTextColor - ) { - metrics.track(Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.CLOSE_TAB)) - onItemTapped(TabCounterMenuItem.CloseTab) - }, - BrowserMenuDivider(), BrowserMenuImageText( label = context.getString(R.string.browser_menu_new_tab), imageResource = R.drawable.ic_new, @@ -108,9 +99,26 @@ class TabCounterToolbarButton( ) { metrics.track(Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.NEW_PRIVATE_TAB)) onItemTapped(TabCounterMenuItem.NewTab(true)) + }, + BrowserMenuDivider(), + BrowserMenuImageText( + label = context.getString(R.string.close_tab), + imageResource = R.drawable.ic_close, + iconTintColorResource = primaryTextColor, + textColorResource = primaryTextColor + ) { + metrics.track(Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.CLOSE_TAB)) + onItemTapped(TabCounterMenuItem.CloseTab) } ) - return BrowserMenuBuilder(menuItems).build(context) + + return BrowserMenuBuilder( + if (context.settings().shouldUseBottomToolbar) { + menuItems.reversed() + } else { + menuItems + } + ).build(context) } private val sessionManagerObserver = object : SessionManager.Observer { From ebd9efc7696d4f4094faa7cd16ee52cbe6d31717 Mon Sep 17 00:00:00 2001 From: mozilla-l10n-automation-bot <54512241+mozilla-l10n-automation-bot@users.noreply.github.com> Date: Thu, 25 Jun 2020 21:03:19 -0400 Subject: [PATCH 02/17] Import l10n. (#11981) --- app/src/main/res/values-hi-rIN/strings.xml | 5 +++-- app/src/main/res/values-it/strings.xml | 4 +--- app/src/main/res/values-pt-rBR/strings.xml | 4 +--- app/src/main/res/values-th/strings.xml | 15 ++++++++++++--- app/src/main/res/values-ur/strings.xml | 4 ++++ 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml index 3cdeb3c5a..0a9bea242 100644 --- a/app/src/main/res/values-hi-rIN/strings.xml +++ b/app/src/main/res/values-hi-rIN/strings.xml @@ -362,6 +362,9 @@ खाता मिटायें + + + firefox.com/pair पर दिखाए गए QR कोड को स्कैन करें]]> कैमरा खोले @@ -825,8 +828,6 @@ साइट की अनुमति ब्राउज़िंग डेटा मिटाएं - - ब्राउज़िंग इतिहास निकास diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 74815fc75..f05855792 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -313,7 +313,7 @@ Inserire un nome per il dispositivo. - Sincronizzazione… + Sincronizzazione in corso… Sincronizzazione non riuscita. Ultima sincronizzazione: %s @@ -922,8 +922,6 @@ Elimina automaticamente i dati di navigazione quando viene selezionato “Esci” nel menu principale Elimina automaticamente i dati di navigazione quando viene selezionato “Esci” nel menu principale - - Cronologia di navigazione Esci diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 96dc03335..ce32f8d82 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -367,7 +367,7 @@ Experimentos - Permite que o Mozilla instale e colete dados para recursos experimentais + Permitir que a Mozilla instale e colete dados para recursos experimentais Relator de travamentos @@ -887,8 +887,6 @@ Excluir dados de navegação automaticamente ao selecionar "Sair" no menu principal. Excluir automaticamente os dados de navegação quando você selecionar \"Sair\" no menu principal. - - Histórico de navegação Sair diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index ba7a4b57c..68c9d17b8 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -874,8 +874,6 @@ ลบข้อมูลการเรียกดูโดยอัตโนมัติเมื่อคุณเลือก "ออก" จากเมนูหลัก ลบข้อมูลการเรียกดูโดยอัตโนมัติเมื่อคุณเลือก \"ออก\" จากเมนูหลัก - - ประวัติการเรียกดู ออก @@ -900,6 +898,9 @@ Firefox Nightly อัปเดตทุกคืนและมีลูกเล่นใหม่ ๆ ทุกคืน         อย่างไรก็ตามความเสถียรอาจลดลง ดาวน์โหลดเบราว์เซอร์เบต้าของเราเพื่อประสบการณ์ที่เสถียรยิ่งขึ้น + + รับ Firefox Beta สำหรับ Android + Firefox Nightly ย้ายไปแล้ว @@ -951,6 +952,8 @@ Sync เปิดอยู่ ไม่สามารถเข้าสู่ระบบ + + ความเป็นส่วนตัวอัตโนมัติ เข้มงวด (แนะนำ) @@ -1379,4 +1382,10 @@ เชื่อมต่ออุปกรณ์อื่น - + + ลงชื่อเข้าใช้เพื่อซิงค์ + + + ตกลง เข้าใจแล้ว + + diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index c2265793b..ac2cddb6c 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -1288,6 +1288,8 @@ “%s” نام والا تلاش انجن پہلے سے موجود ہے۔ ایک تلاشی سٹرنگ داخل کریں + + چیک کریں کہ سرچ سٹرنگ مثال کی شکل سے میل کھاتا ہے ”%s“ سے رابطہ کرنے میں خرابی @@ -1381,6 +1383,8 @@ برائے مہربانی دوبارہ توثیق کریں۔ براہ کرم ٹیب sync کو فعال کریں۔ + + دیگر آلات سے ٹیبیں کی فہرست دیکھیں۔ sync کے لئے سائن ان کریں From 216da0c64b968b77960240351f31a210eec04714 Mon Sep 17 00:00:00 2001 From: mcarare Date: Wed, 29 Apr 2020 15:34:36 +0300 Subject: [PATCH 03/17] For #10285: Add a custom TextView for links with a11y improvements. --- .../org/mozilla/fenix/utils/LinkTextView.kt | 34 +++++++++++++++++++ app/src/main/res/values/strings.xml | 3 +- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/mozilla/fenix/utils/LinkTextView.kt diff --git a/app/src/main/java/org/mozilla/fenix/utils/LinkTextView.kt b/app/src/main/java/org/mozilla/fenix/utils/LinkTextView.kt new file mode 100644 index 000000000..88df32c64 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/utils/LinkTextView.kt @@ -0,0 +1,34 @@ +/* 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.utils + +import android.content.Context +import android.util.AttributeSet +import android.view.accessibility.AccessibilityNodeInfo +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK +import androidx.appcompat.widget.AppCompatTextView +import org.mozilla.fenix.R + +/** + * An [AppCompatTextView] that announces as link in screen readers for a11y purposes + */ +class LinkTextView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : AppCompatTextView(context, attrs, defStyleAttr) { + + override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) { + super.onInitializeAccessibilityNodeInfo(info) + val extras = info?.extras + extras?.putCharSequence( + "AccessibilityNodeInfo.roleDescription", + context.resources.getString(R.string.link_text_view_type_announcement) + ) + // disable long click announcement, as there is no action to be performed on long click + info?.isLongClickable = false + info?.removeAction(ACTION_LONG_CLICK) + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 408eb6093..5ea444599 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1418,5 +1418,6 @@ To add a new top site, remove one. Long press the site and select remove. OK, Got It - + + link From 0626384baf23bfca5c249ae4c30621edba7e88f4 Mon Sep 17 00:00:00 2001 From: mcarare Date: Mon, 25 May 2020 12:37:18 +0300 Subject: [PATCH 04/17] For #10285: Swap TextView for a LinkTextView to improve a11y. --- app/src/main/res/layout/private_browsing_description.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/private_browsing_description.xml b/app/src/main/res/layout/private_browsing_description.xml index 43040abab..d0df46bd0 100644 --- a/app/src/main/res/layout/private_browsing_description.xml +++ b/app/src/main/res/layout/private_browsing_description.xml @@ -25,7 +25,7 @@ android:textSize="14sp" tools:text="@string/private_browsing_placeholder_description_2" /> - Date: Thu, 25 Jun 2020 14:01:56 -0700 Subject: [PATCH 05/17] No issue: Allow us to run experiments on nightly if needed --- app/src/main/java/org/mozilla/fenix/FenixApplication.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index a373570ce..0a5f9c77b 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -37,8 +37,10 @@ import mozilla.components.support.rusthttp.RustHttpConfig import mozilla.components.support.rustlog.RustLog import mozilla.components.support.utils.logElapsedTime import mozilla.components.support.webextensions.WebExtensionSupport +import org.mozilla.fenix.StrictModeManager.enableStrictMode import org.mozilla.fenix.components.Components import org.mozilla.fenix.components.metrics.MetricServiceType +import org.mozilla.fenix.ext.resetPoliciesAfter import org.mozilla.fenix.ext.settings import org.mozilla.fenix.perf.StartupTimeline import org.mozilla.fenix.push.PushFxaIntegration @@ -47,8 +49,6 @@ import org.mozilla.fenix.session.PerformanceActivityLifecycleCallbacks import org.mozilla.fenix.session.VisibilityLifecycleCallback import org.mozilla.fenix.utils.BrowsersCache import org.mozilla.fenix.utils.Settings -import org.mozilla.fenix.StrictModeManager.enableStrictMode -import org.mozilla.fenix.ext.resetPoliciesAfter /** *The main application class for Fenix. Records data to measure initialization performance. @@ -169,7 +169,7 @@ open class FenixApplication : LocaleAwareApplication() { // Enable the service-experiments component to be initialized after visual completeness // for performance wins. - if (settings().isExperimentationEnabled && Config.channel.isReleaseOrBeta) { + if (settings().isExperimentationEnabled) { taskQueue.runIfReadyOrQueue { Experiments.initialize( applicationContext = applicationContext, From 46c1c130da5b377905eaea9dabd01357b2de222c Mon Sep 17 00:00:00 2001 From: Mihai Branescu Date: Thu, 16 Apr 2020 18:18:35 +0300 Subject: [PATCH 06/17] For #9425 - Handle undo for custom search engines On undo, get the template that the user entered for that engine and then re-add it to the `CustomSearchEngineStore` --- .../searchengine/FenixSearchEngineProvider.kt | 22 +++++--- .../search/SearchEngineListPreference.kt | 9 ++- .../FenixSearchEngineProviderTest.kt | 56 +++++++++++++++---- 3 files changed, 63 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt b/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt index d838f2c9f..e8c68eb17 100644 --- a/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt +++ b/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt @@ -135,24 +135,28 @@ open class FenixSearchEngineProvider( return installedSearchEngines(context) } - fun installSearchEngine(context: Context, searchEngine: SearchEngine) = runBlocking { - val installedIdentifiers = installedSearchEngineIdentifiers(context).toMutableSet() - installedIdentifiers.add(searchEngine.identifier) - prefs(context).edit().putStringSet(localeAwareInstalledEnginesKey(), installedIdentifiers).apply() + fun installSearchEngine(context: Context, searchEngine: SearchEngine, isCustom: Boolean = false) = runBlocking { + if (isCustom) { + val searchUrl = searchEngine.getSearchTemplate() + CustomSearchEngineStore.addSearchEngine(context, searchEngine.name, searchUrl) + reload() + } else { + val installedIdentifiers = installedSearchEngineIdentifiers(context).toMutableSet() + installedIdentifiers.add(searchEngine.identifier) + prefs(context).edit() + .putStringSet(localeAwareInstalledEnginesKey(), installedIdentifiers).apply() + } } - fun uninstallSearchEngine(context: Context, searchEngine: SearchEngine) = runBlocking { - val isCustom = CustomSearchEngineStore.isCustomSearchEngine(context, searchEngine.identifier) - + fun uninstallSearchEngine(context: Context, searchEngine: SearchEngine, isCustom: Boolean = false) = runBlocking { if (isCustom) { CustomSearchEngineStore.removeSearchEngine(context, searchEngine.identifier) + reload() } else { val installedIdentifiers = installedSearchEngineIdentifiers(context).toMutableSet() installedIdentifiers.remove(searchEngine.identifier) prefs(context).edit().putStringSet(localeAwareInstalledEnginesKey(), installedIdentifiers).apply() } - - reload() } fun reload() { diff --git a/app/src/main/java/org/mozilla/fenix/settings/search/SearchEngineListPreference.kt b/app/src/main/java/org/mozilla/fenix/settings/search/SearchEngineListPreference.kt index 93e47bd0d..63a88d1f2 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/search/SearchEngineListPreference.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/search/SearchEngineListPreference.kt @@ -18,7 +18,10 @@ import androidx.core.view.isVisible import androidx.navigation.Navigation import androidx.preference.Preference import androidx.preference.PreferenceViewHolder -import kotlinx.android.synthetic.main.search_engine_radio_button.view.* +import kotlinx.android.synthetic.main.search_engine_radio_button.view.engine_icon +import kotlinx.android.synthetic.main.search_engine_radio_button.view.engine_text +import kotlinx.android.synthetic.main.search_engine_radio_button.view.overflow_menu +import kotlinx.android.synthetic.main.search_engine_radio_button.view.radio_button import kotlinx.coroutines.MainScope import mozilla.components.browser.search.SearchEngine import mozilla.components.browser.search.provider.SearchEngineList @@ -167,7 +170,7 @@ abstract class SearchEngineListPreference @JvmOverloads constructor( val initialEngineList = searchEngineList.copy() val initialDefaultEngine = searchEngineList.default - context.components.search.provider.uninstallSearchEngine(context, engine) + context.components.search.provider.uninstallSearchEngine(context, engine, isCustomSearchEngine) MainScope().allowUndo( view = context.getRootView()!!, @@ -175,7 +178,7 @@ abstract class SearchEngineListPreference @JvmOverloads constructor( .getString(R.string.search_delete_search_engine_success_message, engine.name), undoActionTitle = context.getString(R.string.snackbar_deleted_undo), onCancel = { - context.components.search.provider.installSearchEngine(context, engine) + context.components.search.provider.installSearchEngine(context, engine, isCustomSearchEngine) searchEngineList = initialEngineList.copy( default = initialDefaultEngine diff --git a/app/src/test/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProviderTest.kt b/app/src/test/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProviderTest.kt index 82726fcce..41d4532b6 100644 --- a/app/src/test/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProviderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProviderTest.kt @@ -3,6 +3,14 @@ package org.mozilla.fenix.components.searchengine import android.content.Context import io.mockk.every import io.mockk.mockk +import android.graphics.Bitmap +import io.mockk.Runs +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.mockkObject import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -27,6 +35,12 @@ class FenixSearchEngineProviderTest { @Before fun before() { fenixSearchEngineProvider = FakeFenixSearchEngineProvider(testContext) + mockkObject(CustomSearchEngineStore) + fenixSearchEngineProvider.let { + every { CustomSearchEngineStore.loadCustomSearchEngines(testContext) } returns listOf( + (it as FakeFenixSearchEngineProvider).mockSearchEngine("my custom site", "my custom site") + ) + } } /* @@ -37,6 +51,28 @@ class FenixSearchEngineProviderTest { - the above after adding/removing */ + @Suppress("DEPRECATION") + @Test + fun `add custom engine`() = runBlockingTest { + val engineName = "Ecosia" + val engineQuery = "www.ecosia.com/%s" + val searchEngine: SearchEngine = mockk(relaxed = true) + every { searchEngine.getSearchTemplate() } returns engineQuery + every { searchEngine.name } returns engineName + mockkObject(CustomSearchEngineStore) + coEvery { + CustomSearchEngineStore.addSearchEngine( + testContext, + engineName, + engineQuery + ) + } just Runs + + fenixSearchEngineProvider.installSearchEngine(testContext, searchEngine, true) + + coVerify { CustomSearchEngineStore.addSearchEngine(testContext, engineName, engineQuery) } + } + @Test fun `GIVEN sharedprefs does not contain installed engines WHEN installedSearchEngineIdentifiers THEN defaultEngines + customEngines ids are returned`() = runBlockingTest { val expectedDefaults = fenixSearchEngineProvider.baseSearchEngines.toIdSet() @@ -96,21 +132,17 @@ class FakeFenixSearchEngineProvider(context: Context) : FenixSearchEngineProvide ) ) - override var customSearchEngines: Deferred - get() { - return CompletableDeferred( - SearchEngineList( - listOf( - mockSearchEngine("my custom site", "my custom site") - ), default = null - ) - ) - } - set(_) = throw NotImplementedError("Setting not currently supported on this fake") + override var customSearchEngines: Deferred = CompletableDeferred( + SearchEngineList( + listOf( + mockSearchEngine("my custom site", "my custom site") + ), default = null + ) + ) override fun updateBaseSearchEngines() { } - private fun mockSearchEngine( + fun mockSearchEngine( id: String, n: String = id ): SearchEngine { From e6e3166d2d42b98747295248d6b216748606a20a Mon Sep 17 00:00:00 2001 From: Mihai Branescu Date: Wed, 13 May 2020 14:50:45 +0300 Subject: [PATCH 07/17] For #9425 - SearchFragment now reacts to undo operation for custom engines When you remove a search engine from our list, an undo snackbar that outlives the fragment is shown. If you go back to the search fragment and press undo, now the shortcut list gets updated --- .../searchengine/CustomSearchEngineStore.kt | 4 +--- .../org/mozilla/fenix/search/SearchFragment.kt | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/components/searchengine/CustomSearchEngineStore.kt b/app/src/main/java/org/mozilla/fenix/components/searchengine/CustomSearchEngineStore.kt index a5fbf5ad4..555954082 100644 --- a/app/src/main/java/org/mozilla/fenix/components/searchengine/CustomSearchEngineStore.kt +++ b/app/src/main/java/org/mozilla/fenix/components/searchengine/CustomSearchEngineStore.kt @@ -6,7 +6,6 @@ package org.mozilla.fenix.components.searchengine import android.content.Context import android.content.SharedPreferences -import androidx.annotation.VisibleForTesting import mozilla.components.browser.icons.IconRequest import mozilla.components.browser.search.SearchEngine import mozilla.components.browser.search.SearchEngineParser @@ -125,7 +124,6 @@ object CustomSearchEngineStore { } } - private const val PREF_KEY_CUSTOM_SEARCH_ENGINES = "pref_custom_search_engines" - @VisibleForTesting + const val PREF_KEY_CUSTOM_SEARCH_ENGINES = "pref_custom_search_engines" const val PREF_FILE_SEARCH_ENGINES = "custom-search-engines" } diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt index 280f411e0..c36e80d9f 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt @@ -46,6 +46,7 @@ import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.searchengine.CustomSearchEngineStore import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getSpannable import org.mozilla.fenix.ext.hideToolbar @@ -54,6 +55,7 @@ import org.mozilla.fenix.ext.settings import org.mozilla.fenix.search.awesomebar.AwesomeBarView import org.mozilla.fenix.search.toolbar.ToolbarView import org.mozilla.fenix.settings.SupportUtils +import org.mozilla.fenix.settings.registerOnSharedPreferenceChangeListener import org.mozilla.fenix.widget.VoiceSearchActivity.Companion.SPEECH_REQUEST_CODE @Suppress("TooManyFunctions", "LargeClass") @@ -128,6 +130,8 @@ class SearchFragment : Fragment(), UserInteractionHandler { awesomeBarView = AwesomeBarView(view.scrollable_area, searchInteractor, view.findViewById(R.id.awesomeBar)) + setShortcutsChangedListener() + view.scrollView.setOnScrollChangeListener { _: NestedScrollView, _: Int, _: Int, _: Int, _: Int -> view.hideKeyboard() @@ -164,6 +168,17 @@ class SearchFragment : Fragment(), UserInteractionHandler { return (speechIntent.resolveActivity(requireContext().packageManager) != null) } + private fun setShortcutsChangedListener() { + requireContext().getSharedPreferences( + CustomSearchEngineStore.PREF_FILE_SEARCH_ENGINES, + Context.MODE_PRIVATE + ).registerOnSharedPreferenceChangeListener(viewLifecycleOwner) { _, key -> + if (key == CustomSearchEngineStore.PREF_KEY_CUSTOM_SEARCH_ENGINES) { + awesomeBarView.update(searchStore.state) + } + } + } + private fun launchVoiceSearch() { // Note if a user disables speech while the app is on the search fragment // the voice button will still be available and *will* cause a crash if tapped, From 93247e0b2f5cfd466f0f1542eb78c93acca77a67 Mon Sep 17 00:00:00 2001 From: Mihai Branescu Date: Wed, 13 May 2020 17:48:02 +0300 Subject: [PATCH 08/17] For #9425 - SearchFragment now reacts to undo operation for base engines --- .../searchengine/CustomSearchEngineStore.kt | 2 +- .../searchengine/FenixSearchEngineProvider.kt | 4 ++-- .../org/mozilla/fenix/search/SearchFragment.kt | 14 +++++++------- .../searchengine/FenixSearchEngineProviderTest.kt | 5 +---- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/components/searchengine/CustomSearchEngineStore.kt b/app/src/main/java/org/mozilla/fenix/components/searchengine/CustomSearchEngineStore.kt index 555954082..ef0c840e5 100644 --- a/app/src/main/java/org/mozilla/fenix/components/searchengine/CustomSearchEngineStore.kt +++ b/app/src/main/java/org/mozilla/fenix/components/searchengine/CustomSearchEngineStore.kt @@ -124,6 +124,6 @@ object CustomSearchEngineStore { } } - const val PREF_KEY_CUSTOM_SEARCH_ENGINES = "pref_custom_search_engines" + private const val PREF_KEY_CUSTOM_SEARCH_ENGINES = "pref_custom_search_engines" const val PREF_FILE_SEARCH_ENGINES = "custom-search-engines" } diff --git a/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt b/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt index e8c68eb17..4775f9e71 100644 --- a/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt +++ b/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt @@ -183,7 +183,7 @@ open class FenixSearchEngineProvider( } private fun prefs(context: Context) = context.getSharedPreferences( - PREF_FILE, + PREF_FILE_SEARCH_ENGINES, Context.MODE_PRIVATE ) @@ -229,7 +229,7 @@ open class FenixSearchEngineProvider( @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) companion object { val BUNDLED_SEARCH_ENGINES = listOf("reddit", "youtube") - const val PREF_FILE = "fenix-search-engine-provider" + const val PREF_FILE_SEARCH_ENGINES = "fenix-search-engine-provider" const val INSTALLED_ENGINES_KEY = "fenix-installed-search-engines" const val CURRENT_LOCALE_KEY = "fenix-current-locale" } diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt index c36e80d9f..2190be332 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt @@ -47,6 +47,7 @@ import org.mozilla.fenix.R import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.searchengine.CustomSearchEngineStore +import org.mozilla.fenix.components.searchengine.FenixSearchEngineProvider import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getSpannable import org.mozilla.fenix.ext.hideToolbar @@ -130,7 +131,8 @@ class SearchFragment : Fragment(), UserInteractionHandler { awesomeBarView = AwesomeBarView(view.scrollable_area, searchInteractor, view.findViewById(R.id.awesomeBar)) - setShortcutsChangedListener() + setShortcutsChangedListener(CustomSearchEngineStore.PREF_FILE_SEARCH_ENGINES) + setShortcutsChangedListener(FenixSearchEngineProvider.PREF_FILE_SEARCH_ENGINES) view.scrollView.setOnScrollChangeListener { _: NestedScrollView, _: Int, _: Int, _: Int, _: Int -> @@ -168,14 +170,12 @@ class SearchFragment : Fragment(), UserInteractionHandler { return (speechIntent.resolveActivity(requireContext().packageManager) != null) } - private fun setShortcutsChangedListener() { + private fun setShortcutsChangedListener(preferenceFileName: String) { requireContext().getSharedPreferences( - CustomSearchEngineStore.PREF_FILE_SEARCH_ENGINES, + preferenceFileName, Context.MODE_PRIVATE - ).registerOnSharedPreferenceChangeListener(viewLifecycleOwner) { _, key -> - if (key == CustomSearchEngineStore.PREF_KEY_CUSTOM_SEARCH_ENGINES) { - awesomeBarView.update(searchStore.state) - } + ).registerOnSharedPreferenceChangeListener(viewLifecycleOwner) { _, _ -> + awesomeBarView.update(searchStore.state) } } diff --git a/app/src/test/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProviderTest.kt b/app/src/test/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProviderTest.kt index 41d4532b6..74327dafd 100644 --- a/app/src/test/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProviderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProviderTest.kt @@ -3,13 +3,10 @@ package org.mozilla.fenix.components.searchengine import android.content.Context import io.mockk.every import io.mockk.mockk -import android.graphics.Bitmap import io.mockk.Runs import io.mockk.coEvery import io.mockk.coVerify -import io.mockk.every import io.mockk.just -import io.mockk.mockk import io.mockk.mockkObject import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred @@ -85,7 +82,7 @@ class FenixSearchEngineProviderTest { @Test fun `GIVEN sharedprefs contains installed engines WHEN installedSearchEngineIdentifiers THEN defaultEngines + customEngines ids are returned`() = runBlockingTest { - val sp = testContext.getSharedPreferences(FenixSearchEngineProvider.PREF_FILE, Context.MODE_PRIVATE) + val sp = testContext.getSharedPreferences(FenixSearchEngineProvider.PREF_FILE_SEARCH_ENGINES, Context.MODE_PRIVATE) sp.edit().putStringSet(fenixSearchEngineProvider.localeAwareInstalledEnginesKey(), persistedInstalledEngines).apply() val expectedStored = persistedInstalledEngines From 9f5c8819d35a74175494a27449327b1f9b5c6d9d Mon Sep 17 00:00:00 2001 From: ValentinTimisica Date: Fri, 26 Jun 2020 14:08:50 +0300 Subject: [PATCH 09/17] For #11892: Remove dynamic calls to setupNavigationToolbar This is a speculative fix for 11892 crash. The side effect is that the back button will be visible in bookmarks root. --- app/src/main/java/org/mozilla/fenix/HomeActivity.kt | 2 +- .../org/mozilla/fenix/library/bookmarks/BookmarkView.kt | 9 --------- .../org/mozilla/fenix/library/history/HistoryView.kt | 3 --- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index e7d548ae2..0a9b5178c 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -367,11 +367,11 @@ open class HomeActivity : LocaleAwareAppCompatActivity() { * Everyone should call this instead of supportActionBar. */ fun getSupportActionBarAndInflateIfNecessary(): ActionBar { - // Add ids to this that we don't want to have a toolbar back button if (!isToolbarInflated) { navigationToolbar = navigationToolbarStub.inflate() as Toolbar setSupportActionBar(navigationToolbar) + // Add ids to this that we don't want to have a toolbar back button setupNavigationToolbar() isToolbarInflated = true diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt index 2500f374e..12216e8a7 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt @@ -13,7 +13,6 @@ import kotlinx.android.synthetic.main.component_bookmark.view.* import mozilla.appservices.places.BookmarkRoot import mozilla.components.concept.storage.BookmarkNode import mozilla.components.support.base.feature.UserInteractionHandler -import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.NavGraphDirections import org.mozilla.fenix.library.LibraryPageView @@ -140,17 +139,9 @@ class BookmarkView( bookmarkAdapter.updateData(state.tree, mode) when (mode) { is BookmarkFragmentState.Mode.Normal -> { - if (tree != null) { - if (BookmarkRoot.Mobile.id == tree?.guid) { - (activity as HomeActivity).setupNavigationToolbar(R.id.bookmarkFragment) - } else { - (activity as HomeActivity).setupNavigationToolbar() - } - } setUiForNormalMode(state.tree) } is BookmarkFragmentState.Mode.Selecting -> { - (activity as HomeActivity).setupNavigationToolbar() setUiForSelectingMode( context.getString( R.string.bookmarks_multi_select_title, diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt index 75c6bce67..f831f3305 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt @@ -13,7 +13,6 @@ import androidx.recyclerview.widget.SimpleItemAnimator import kotlinx.android.synthetic.main.component_history.* import kotlinx.android.synthetic.main.component_history.view.* import mozilla.components.support.base.feature.UserInteractionHandler -import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.library.LibraryPageView import org.mozilla.fenix.library.SelectionInteractor @@ -143,13 +142,11 @@ class HistoryView( when (val mode = state.mode) { is HistoryFragmentState.Mode.Normal -> { - (activity as HomeActivity).setupNavigationToolbar(R.id.historyFragment) setUiForNormalMode( context.getString(R.string.library_history) ) } is HistoryFragmentState.Mode.Editing -> { - (activity as HomeActivity).setupNavigationToolbar() setUiForSelectingMode( context.getString(R.string.history_multi_select_title, mode.selectedItems.size) ) From 64947a8686c443607acd805aec96e08bf3a73800 Mon Sep 17 00:00:00 2001 From: mcarare Date: Fri, 26 Jun 2020 13:10:17 +0300 Subject: [PATCH 10/17] For #10285: Swap TextView for a LinkTextView to improve links a11y --- app/src/main/res/layout/component_exceptions.xml | 2 +- app/src/main/res/layout/component_saved_logins.xml | 2 +- app/src/main/res/layout/custom_search_engine.xml | 3 +-- app/src/main/res/layout/onboarding_whats_new.xml | 2 +- app/src/main/res/layout/search_suggestions_onboarding.xml | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/layout/component_exceptions.xml b/app/src/main/res/layout/component_exceptions.xml index b97bebcb9..00d64c282 100644 --- a/app/src/main/res/layout/component_exceptions.xml +++ b/app/src/main/res/layout/component_exceptions.xml @@ -27,7 +27,7 @@ android:visibility="visible" app:layout_constraintTop_toTopOf="parent" /> - - - - - Date: Fri, 26 Jun 2020 11:04:46 -0700 Subject: [PATCH 11/17] Add tests for OnSharedPreferenceChangeListener (#12019) --- .../org/mozilla/fenix/settings/Extensions.kt | 4 +- .../mozilla/fenix/settings/ExtensionsTest.kt | 103 ++++++++++++++++++ .../OnSharedPreferenceChangeListenerTest.kt | 51 +++++++++ 3 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 app/src/test/java/org/mozilla/fenix/settings/ExtensionsTest.kt create mode 100644 app/src/test/java/org/mozilla/fenix/settings/OnSharedPreferenceChangeListenerTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt b/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt index 98cc7be96..24b6670e8 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/Extensions.kt @@ -10,9 +10,9 @@ import androidx.appcompat.content.res.AppCompatResources import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import mozilla.components.feature.sitepermissions.SitePermissions +import mozilla.components.support.ktx.android.content.res.resolveAttribute import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelative import org.mozilla.fenix.ext.getPreferenceKey -import org.mozilla.fenix.theme.ThemeManager fun SitePermissions.toggle(featurePhone: PhoneFeature): SitePermissions { return update(featurePhone, get(featurePhone).toggle()) @@ -41,7 +41,7 @@ fun SitePermissions.update(field: PhoneFeature, value: SitePermissions.Status) = * as a result we have to apply it programmatically. More info about this issue https://github.com/mozilla-mobile/fenix/issues/1414 */ fun RadioButton.setStartCheckedIndicator() { - val attr = ThemeManager.resolveAttribute(android.R.attr.listChoiceIndicatorSingle, context) + val attr = context.theme.resolveAttribute(android.R.attr.listChoiceIndicatorSingle) val buttonDrawable = AppCompatResources.getDrawable(context, attr) buttonDrawable?.apply { setBounds(0, 0, intrinsicWidth, intrinsicHeight) diff --git a/app/src/test/java/org/mozilla/fenix/settings/ExtensionsTest.kt b/app/src/test/java/org/mozilla/fenix/settings/ExtensionsTest.kt new file mode 100644 index 000000000..3df70af7f --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/ExtensionsTest.kt @@ -0,0 +1,103 @@ +/* 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.graphics.Rect +import android.widget.RadioButton +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.SwitchPreference +import io.mockk.Called +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.verify +import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelative +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner + +@RunWith(FenixRobolectricTestRunner::class) +class ExtensionsTest { + + @MockK(relaxUnitFun = true) private lateinit var radioButton: RadioButton + @MockK private lateinit var fragment: PreferenceFragmentCompat + private lateinit var preference: Preference + + @Before + fun setup() { + MockKAnnotations.init(this) + preference = Preference(testContext) + + every { radioButton.context } returns testContext + every { + fragment.getString(R.string.pref_key_accessibility_force_enable_zoom) + } returns "pref_key_accessibility_force_enable_zoom" + every { + fragment.getString(R.string.pref_key_accessibility_auto_size) + } returns "pref_key_accessibility_auto_size" + } + + @Test + fun `test radiobutton setStartCheckedIndicator`() { + radioButton.setStartCheckedIndicator() + + verify { radioButton.putCompoundDrawablesRelative(start = withArg { + assertEquals(Rect(0, 0, it.intrinsicWidth, it.intrinsicHeight), it.bounds) + }) } + } + + @Test + fun `set change listener with typed argument`() { + val callback = mockk<(Preference, String) -> Unit>(relaxed = true) + preference.setOnPreferenceChangeListener { pref, value -> + callback(pref, value) + true + } + + assertFalse(preference.callChangeListener(10)) + verify { callback wasNot Called } + + assertTrue(preference.callChangeListener("Hello")) + verify { callback(preference, "Hello") } + } + + @Test + fun `requirePreference returns corresponding preference`() { + val switchPreference = mockk() + every { + fragment.findPreference("pref_key_accessibility_auto_size") + } returns switchPreference + + assertEquals( + switchPreference, + fragment.requirePreference(R.string.pref_key_accessibility_auto_size) + ) + } + + @Test + fun `requirePreference throws if null preference is returned`() { + every { + fragment.findPreference("pref_key_accessibility_force_enable_zoom") + } returns null + + var exception: IllegalArgumentException? = null + try { + fragment.requirePreference(R.string.pref_key_accessibility_force_enable_zoom) + } catch (e: IllegalArgumentException) { + exception = e + } + + assertNotNull(exception) + } +} diff --git a/app/src/test/java/org/mozilla/fenix/settings/OnSharedPreferenceChangeListenerTest.kt b/app/src/test/java/org/mozilla/fenix/settings/OnSharedPreferenceChangeListenerTest.kt new file mode 100644 index 000000000..f4f0e32fa --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/OnSharedPreferenceChangeListenerTest.kt @@ -0,0 +1,51 @@ +/* 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.SharedPreferences +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +import io.mockk.Called +import io.mockk.mockk +import io.mockk.verify +import org.junit.Before +import org.junit.Test + +class OnSharedPreferenceChangeListenerTest { + + private lateinit var sharedPrefs: SharedPreferences + private lateinit var listener: (SharedPreferences, String) -> Unit + private lateinit var owner: LifecycleOwner + private lateinit var lifecycleRegistry: LifecycleRegistry + + @Before + fun setup() { + sharedPrefs = mockk(relaxUnitFun = true) + listener = mockk(relaxed = true) + owner = LifecycleOwner { lifecycleRegistry } + lifecycleRegistry = LifecycleRegistry(owner) + } + + @Test + fun `test listener is registered based on lifecycle`() { + sharedPrefs.registerOnSharedPreferenceChangeListener(owner, listener) + verify { sharedPrefs wasNot Called } + + lifecycleRegistry.currentState = Lifecycle.State.CREATED + verify { sharedPrefs.registerOnSharedPreferenceChangeListener(any()) } + + lifecycleRegistry.currentState = Lifecycle.State.DESTROYED + verify { sharedPrefs.unregisterOnSharedPreferenceChangeListener(any()) } + } + + @Test + fun `listener should call lambda`() { + val wrapper = OnSharedPreferenceChangeListener(mockk(), listener) + wrapper.onSharedPreferenceChanged(sharedPrefs, "key") + + verify { listener(sharedPrefs, "key") } + } +} From 005ad6ff2ab0c72784bcba6242cc6545918a4d7c Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Fri, 26 Jun 2020 14:16:57 -0400 Subject: [PATCH 12/17] Update Android Components version to 48.0.20200626130049. (#12006) Co-authored-by: MickeyMoz Co-authored-by: Gabriel Luong --- .../org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt | 2 +- .../main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt | 3 ++- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt index bc0e49ae5..427ec0306 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt @@ -70,7 +70,7 @@ class DefaultToolbarMenu( onAddonsManagerTapped = { onItemTapped.invoke(ToolbarMenu.Item.AddonsManager) }, - appendExtensionActionAtStart = !shouldReverseItems + appendExtensionSubMenuAtStart = !shouldReverseItems ) } diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt index 9745caa8a..e8ac2a019 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt @@ -20,6 +20,7 @@ import mozilla.components.concept.tabstray.TabsTray import mozilla.components.feature.media.ext.pauseIfPlaying import mozilla.components.feature.media.ext.playIfPaused import mozilla.components.support.base.observer.Observable +import mozilla.components.support.images.ImageRequest import mozilla.components.support.images.loader.ImageLoader import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl import org.mozilla.fenix.R @@ -71,7 +72,7 @@ class TabTrayViewHolder( if (tab.thumbnail != null) { thumbnailView.setImageBitmap(tab.thumbnail) } else { - imageLoader.loadIntoView(thumbnailView, tab.id) + imageLoader.loadIntoView(thumbnailView, ImageRequest(tab.id)) } // Media state diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 372ee2727..eb09f595f 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "48.0.20200625130125" + const val VERSION = "48.0.20200626130049" } From f3b44c06e4bf166f14c31e8ab5d53cc171cdc08c Mon Sep 17 00:00:00 2001 From: MarcLeclair Date: Fri, 26 Jun 2020 14:54:20 -0400 Subject: [PATCH 13/17] For #11841: Removed topsite bug on private browsing (#12020) --- .../java/org/mozilla/fenix/home/HomeFragment.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index be44712c2..76c463054 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -248,11 +248,17 @@ class HomeFragment : Fragment() { * data in our store. The [View.consumeFrom] coroutine dispatch * doesn't get run right away which means that we won't draw on the first layout pass. */ - fun updateSessionControlView(view: View) { - sessionControlView?.update(homeFragmentStore.state) + private fun updateSessionControlView(view: View) { + if (browsingModeManager.mode == BrowsingMode.Private) { + view.consumeFrom(homeFragmentStore, viewLifecycleOwner) { + sessionControlView?.update(it) + } + } else { + sessionControlView?.update(homeFragmentStore.state) - view.consumeFrom(homeFragmentStore, viewLifecycleOwner) { - sessionControlView?.update(it) + view.consumeFrom(homeFragmentStore, viewLifecycleOwner) { + sessionControlView?.update(it) + } } } From bcb2ef10acb219e32d26be5478bda9554e1a7f84 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Fri, 26 Jun 2020 12:12:52 -0700 Subject: [PATCH 14/17] Add tests for PhoneFeature (#12021) --- .../mozilla/fenix/settings/PhoneFeature.kt | 37 ++++--- .../fenix/settings/PhoneFeatureTest.kt | 98 +++++++++++++++++++ 2 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 app/src/test/java/org/mozilla/fenix/settings/PhoneFeatureTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt b/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt index 7ee4790fc..6031fc151 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/PhoneFeature.kt @@ -42,28 +42,25 @@ enum class PhoneFeature(val androidPermissionsList: Array) : Parcelable sitePermissions: SitePermissions? = null, settings: Settings? = null ): String { - @StringRes val stringRes = - when (isAndroidPermissionGranted(context)) { - false -> R.string.phone_feature_blocked_by_android - else -> when (this) { - AUTOPLAY_AUDIBLE -> { - when (settings?.getAutoplayUserSetting(default = AUTOPLAY_BLOCK_ALL) ?: AUTOPLAY_BLOCK_ALL) { - AUTOPLAY_ALLOW_ALL -> R.string.preference_option_autoplay_allowed2 - AUTOPLAY_ALLOW_ON_WIFI -> R.string.preference_option_autoplay_allowed_wifi_only2 - AUTOPLAY_BLOCK_AUDIBLE -> R.string.preference_option_autoplay_block_audio2 - AUTOPLAY_BLOCK_ALL -> R.string.preference_option_autoplay_blocked3 - else -> R.string.preference_option_autoplay_blocked3 - } - } - else -> { - when (getStatus(sitePermissions, settings)) { - SitePermissions.Status.BLOCKED -> R.string.preference_option_phone_feature_blocked - SitePermissions.Status.NO_DECISION -> R.string.preference_option_phone_feature_ask_to_allow - SitePermissions.Status.ALLOWED -> R.string.preference_option_phone_feature_allowed - } + @StringRes val stringRes = if (isAndroidPermissionGranted(context)) { + when (this) { + AUTOPLAY_AUDIBLE -> + when (settings?.getAutoplayUserSetting(default = AUTOPLAY_BLOCK_ALL) ?: AUTOPLAY_BLOCK_ALL) { + AUTOPLAY_ALLOW_ALL -> R.string.preference_option_autoplay_allowed2 + AUTOPLAY_ALLOW_ON_WIFI -> R.string.preference_option_autoplay_allowed_wifi_only2 + AUTOPLAY_BLOCK_AUDIBLE -> R.string.preference_option_autoplay_block_audio2 + AUTOPLAY_BLOCK_ALL -> R.string.preference_option_autoplay_blocked3 + else -> R.string.preference_option_autoplay_blocked3 } + else -> when (getStatus(sitePermissions, settings)) { + SitePermissions.Status.BLOCKED -> R.string.preference_option_phone_feature_blocked + SitePermissions.Status.NO_DECISION -> R.string.preference_option_phone_feature_ask_to_allow + SitePermissions.Status.ALLOWED -> R.string.preference_option_phone_feature_allowed } } + } else { + R.string.phone_feature_blocked_by_android + } return context.getString(stringRes) } @@ -109,7 +106,7 @@ enum class PhoneFeature(val androidPermissionsList: Array) : Parcelable fun getAction(settings: Settings): SitePermissionsRules.Action = settings.getSitePermissionsPhoneFeatureAction(this, getDefault()) - fun getDefault(): SitePermissionsRules.Action { + private fun getDefault(): SitePermissionsRules.Action { return when (this) { AUTOPLAY_AUDIBLE -> SitePermissionsRules.Action.BLOCKED AUTOPLAY_INAUDIBLE -> SitePermissionsRules.Action.ALLOWED diff --git a/app/src/test/java/org/mozilla/fenix/settings/PhoneFeatureTest.kt b/app/src/test/java/org/mozilla/fenix/settings/PhoneFeatureTest.kt new file mode 100644 index 000000000..4980d5080 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/PhoneFeatureTest.kt @@ -0,0 +1,98 @@ +/* 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.Manifest +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import mozilla.components.feature.sitepermissions.SitePermissions +import mozilla.components.feature.sitepermissions.SitePermissions.Status +import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.utils.Settings + +@RunWith(FenixRobolectricTestRunner::class) +class PhoneFeatureTest { + + @MockK private lateinit var sitePermissions: SitePermissions + @MockK private lateinit var settings: Settings + + @Before + fun setup() { + MockKAnnotations.init(this) + } + + @Test + fun `getStatus throws if both values are null`() { + var exception: IllegalArgumentException? = null + try { + PhoneFeature.AUTOPLAY_AUDIBLE.getStatus() + } catch (e: java.lang.IllegalArgumentException) { + exception = e + } + assertNotNull(exception) + } + + @Test + fun `getStatus returns value from site permissions`() { + every { sitePermissions.notification } returns Status.BLOCKED + assertEquals(Status.BLOCKED, PhoneFeature.NOTIFICATION.getStatus(sitePermissions, settings)) + } + + @Test + fun `getStatus returns value from settings`() { + every { + settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.AUTOPLAY_INAUDIBLE, Action.ALLOWED) + } returns Action.ALLOWED + assertEquals(Status.ALLOWED, PhoneFeature.AUTOPLAY_INAUDIBLE.getStatus(settings = settings)) + } + + @Test + fun getLabel() { + assertEquals("Camera", PhoneFeature.CAMERA.getLabel(testContext)) + assertEquals("Location", PhoneFeature.LOCATION.getLabel(testContext)) + assertEquals("Microphone", PhoneFeature.MICROPHONE.getLabel(testContext)) + assertEquals("Notification", PhoneFeature.NOTIFICATION.getLabel(testContext)) + assertEquals("Autoplay", PhoneFeature.AUTOPLAY_AUDIBLE.getLabel(testContext)) + assertEquals("Autoplay", PhoneFeature.AUTOPLAY_INAUDIBLE.getLabel(testContext)) + } + + @Test + fun getPreferenceId() { + assertEquals(R.string.pref_key_phone_feature_camera, PhoneFeature.CAMERA.getPreferenceId()) + assertEquals(R.string.pref_key_phone_feature_location, PhoneFeature.LOCATION.getPreferenceId()) + assertEquals(R.string.pref_key_phone_feature_microphone, PhoneFeature.MICROPHONE.getPreferenceId()) + assertEquals(R.string.pref_key_phone_feature_notification, PhoneFeature.NOTIFICATION.getPreferenceId()) + assertEquals(R.string.pref_key_browser_feature_autoplay_audible, PhoneFeature.AUTOPLAY_AUDIBLE.getPreferenceId()) + assertEquals(R.string.pref_key_browser_feature_autoplay_inaudible, PhoneFeature.AUTOPLAY_INAUDIBLE.getPreferenceId()) + + assertEquals( + "pref_key_browser_feature_autoplay_inaudible", + PhoneFeature.AUTOPLAY_INAUDIBLE.getPreferenceKey(testContext) + ) + } + + @Test + fun `getAction returns value from settings`() { + every { + settings.getSitePermissionsPhoneFeatureAction(PhoneFeature.AUTOPLAY_AUDIBLE, Action.BLOCKED) + } returns Action.ASK_TO_ALLOW + assertEquals(Action.ASK_TO_ALLOW, PhoneFeature.AUTOPLAY_AUDIBLE.getAction(settings)) + } + + @Test + fun findFeatureBy() { + assertEquals(PhoneFeature.CAMERA, PhoneFeature.findFeatureBy(arrayOf(Manifest.permission.CAMERA))) + assertEquals(PhoneFeature.MICROPHONE, PhoneFeature.findFeatureBy(arrayOf(Manifest.permission.RECORD_AUDIO))) + } +} From eeabcb10ff7ba54b696e19361a3523699c1f6356 Mon Sep 17 00:00:00 2001 From: Elise Richards Date: Fri, 26 Jun 2020 16:12:18 -0500 Subject: [PATCH 15/17] Fix illegal arguments for onboarding keys (#12033) --- app/metrics.yaml | 2 +- .../org/mozilla/fenix/components/metrics/Metrics.kt | 13 +++++++------ docs/metrics.md | 10 +++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/metrics.yaml b/app/metrics.yaml index 6436d9afa..a56e2fba1 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -276,7 +276,7 @@ onboarding: description: The tracking protection preference was chosen from the onboarding card. extra_keys: - position: + setting: description: | A string that indicates the Tracking Protection policy STANDARD or STRICT. Default: Toggle ON, STANDARD diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/Metrics.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/Metrics.kt index a14351a02..46b1ea9e1 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/Metrics.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/Metrics.kt @@ -37,6 +37,7 @@ import org.mozilla.fenix.GleanMetrics.CrashReporter import org.mozilla.fenix.GleanMetrics.ErrorPage import org.mozilla.fenix.GleanMetrics.Events import org.mozilla.fenix.GleanMetrics.Logins +import org.mozilla.fenix.GleanMetrics.Onboarding import org.mozilla.fenix.GleanMetrics.PerfAwesomebar import org.mozilla.fenix.GleanMetrics.SearchShortcuts import org.mozilla.fenix.GleanMetrics.Tip @@ -188,22 +189,22 @@ sealed class Event { data class OnboardingToolbarPosition(val position: Position) : Event() { enum class Position { TOP, BOTTOM } - override val extras: Map? - get() = hashMapOf(ToolbarSettings.changedPositionKeys.position to position.name) + override val extras: Map? + get() = hashMapOf(Onboarding.prefToggledToolbarPositionKeys.position to position.name) } data class OnboardingTrackingProtection(val setting: Setting) : Event() { enum class Setting { STRICT, STANDARD } - override val extras: Map? - get() = hashMapOf(TrackingProtection.etpSettingChangedKeys.etpSetting to setting.name) + override val extras: Map? + get() = hashMapOf(Onboarding.prefToggledTrackingProtKeys.setting to setting.name) } data class OnboardingThemePicker(val theme: Theme) : Event() { enum class Theme { LIGHT, DARK, FOLLOW_DEVICE } - override val extras: Map? - get() = mapOf(AppTheme.darkThemeSelectedKeys.source to theme.name) + override val extras: Map? + get() = mapOf(Onboarding.prefToggledThemePickerKeys.theme to theme.name) } data class PreferenceToggled( diff --git a/docs/metrics.md b/docs/metrics.md index 0c0cd6540..69aa677cf 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -82,10 +82,10 @@ The following metrics are added to the ping: | collections.tab_select_opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the select tabs screen (the first step of the collection creation flow) |[1](https://github.com/mozilla-mobile/fenix/pull/3935)||2020-09-01 | | collections.tabs_added |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user saved a list of tabs to an existing collection |[1](https://github.com/mozilla-mobile/fenix/pull/3935)|
  • tabs_open: The number of tabs open in the current session
  • tabs_selected: The number of tabs added to the collection
|2020-09-01 | | context_menu.item_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped an item in the browsers context menu |[1](https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010)|
  • named: The name of the item that was tapped. Available items are: ``` open_in_new_tab, open_in_private_tab, open_image_in_new_tab, save_image, share_link, copy_link, copy_image_location ```
|2020-09-01 | -| contextual_hint.tracking_protection.dismiss |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The enhanced tracking protection contextual hint was dismissed by pressing the close button |[1](https://github.com/mozilla-mobile/fenix/pull/TODO)||2020-09-01 | -| contextual_hint.tracking_protection.display |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The enhanced tracking protection contextual hint was displayed. |[1](https://github.com/mozilla-mobile/fenix/pull/TODO)||2020-09-01 | -| contextual_hint.tracking_protection.inside_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user tapped inside of the etp contextual hint (which brings up the etp panel for this site). |[1](https://github.com/mozilla-mobile/fenix/pull/TODO)||2020-09-01 | -| contextual_hint.tracking_protection.outside_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user tapped outside of the etp contextual hint (which has no effect). |[1](https://github.com/mozilla-mobile/fenix/pull/TODO)||2020-09-01 | +| contextual_hint.tracking_protection.dismiss |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The enhanced tracking protection contextual hint was dismissed by pressing the close button |[1](https://github.com/mozilla-mobile/fenix/pull/11923)||2020-09-01 | +| contextual_hint.tracking_protection.display |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The enhanced tracking protection contextual hint was displayed. |[1](https://github.com/mozilla-mobile/fenix/pull/11923)||2020-09-01 | +| contextual_hint.tracking_protection.inside_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user tapped inside of the etp contextual hint (which brings up the etp panel for this site). |[1](https://github.com/mozilla-mobile/fenix/pull/11923)||2020-09-01 | +| contextual_hint.tracking_protection.outside_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user tapped outside of the etp contextual hint (which has no effect). |[1](https://github.com/mozilla-mobile/fenix/pull/11923)||2020-09-01 | | crash_reporter.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The crash reporter was closed |[1](https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708)|
  • crash_submitted: A boolean that tells us whether or not the user submitted a crash report
|2020-09-01 | | crash_reporter.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The crash reporter was displayed |[1](https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708)||2020-09-01 | | custom_tab.action_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the action button provided by the launching app |[1](https://github.com/mozilla-mobile/fenix/pull/1697)||2020-09-01 | @@ -132,7 +132,7 @@ The following metrics are added to the ping: | onboarding.pref_toggled_private_browsing |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The private browsing preference was selected from the onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-09-01 | | onboarding.pref_toggled_theme_picker |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The device theme was chosen using the theme picker onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)|
  • theme: A string that indicates the theme LIGHT, DARK, or FOLLOW DEVICE. Default: FOLLOW DEVICE
|2020-09-01 | | onboarding.pref_toggled_toolbar_position |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The toolbar position preference was chosen from the onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)|
  • position: A string that indicates the position of the toolbar TOP or BOTTOM. Default: BOTTOM
|2020-09-01 | -| onboarding.pref_toggled_tracking_prot |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tracking protection preference was chosen from the onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)|
  • position: A string that indicates the Tracking Protection policy STANDARD or STRICT. Default: Toggle ON, STANDARD
|2020-09-01 | +| onboarding.pref_toggled_tracking_prot |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tracking protection preference was chosen from the onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)|
  • setting: A string that indicates the Tracking Protection policy STANDARD or STRICT. Default: Toggle ON, STANDARD
|2020-09-01 | | onboarding.privacy_notice |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The onboarding privacy notice card was tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-09-01 | | onboarding.whats_new |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The onboarding What\'s New card was tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-09-01 | | pocket.pocket_top_site_clicked |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user clicked on the trending Pocket top site |[1](https://github.com/mozilla-mobile/fenix/pull/8098)||2020-09-01 | From f8cb1d6b485deb40261ac5e1c1de3259b201ce9e Mon Sep 17 00:00:00 2001 From: Grisha Kruglov Date: Thu, 25 Jun 2020 17:35:52 -0700 Subject: [PATCH 16/17] Closes #11909: Metrics for recording number of recently used PWAs --- app/metrics.yaml | 37 +++++++++++++++++++ .../java/org/mozilla/fenix/HomeActivity.kt | 26 +++++++++++++ docs/metrics.md | 2 + 3 files changed, 65 insertions(+) diff --git a/app/metrics.yaml b/app/metrics.yaml index a56e2fba1..952ab58c3 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -497,6 +497,43 @@ metrics: notification_emails: - fenix-core@mozilla.com expires: "2020-09-01" + recently_used_pwa_count: + type: counter + lifetime: application + description: | + A counter that indicates how many PWAs a user has recently used. + Threshold for "recency" set in HomeActivity#PWA_RECENTLY_USED_THRESHOLD. + Currently we are not told by the OS when a PWA is removed by the user, + so we use the "recently used" heuristic to judge how many PWAs are still + active, as a proxy for "installed". This value will only be set if the + user has at least *one* recently used PWA. If they have 0, this metric + will not be sent, resulting in a null value during analysis on the + server-side. To disambiguate between a failed `recently_used_pwa_count` + metric and 0 recent PWAs, please see `has_recent_pwas`. + send_in_pings: + - metrics + bugs: + - https://github.com/mozilla-mobile/fenix/issues/11909 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/11982#pullrequestreview-437963817 + notification_emails: + - fenix-core@mozilla.com + expires: "2020-12-01" + has_recent_pwas: + type: boolean + lifetime: application + description: | + A boolean that indicates if the user has recently used PWAs. + See recently_used_pwa_count for the actual count. + send_in_pings: + - metrics + bugs: + - https://github.com/mozilla-mobile/fenix/issues/11909 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/11982#pullrequestreview-437963817 + notification_emails: + - fenix-core@mozilla.com + expires: "2020-12-01" search_count: type: labeled_counter description: | diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index 0a9b5178c..a80def5b0 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -8,6 +8,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.os.StrictMode +import android.text.format.DateUtils import android.util.AttributeSet import android.view.View import android.view.WindowManager @@ -27,6 +28,7 @@ import androidx.navigation.ui.NavigationUI import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.android.synthetic.main.activity_home.* import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import mozilla.components.browser.search.SearchEngine import mozilla.components.browser.session.Session @@ -50,6 +52,7 @@ import mozilla.components.support.locale.LocaleAwareAppCompatActivity import mozilla.components.support.utils.SafeIntent import mozilla.components.support.utils.toSafeIntent import mozilla.components.support.webextensions.WebExtensionPopupFeature +import org.mozilla.fenix.GleanMetrics.Metrics import org.mozilla.fenix.browser.UriOpenedObserver import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager @@ -188,6 +191,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity() { intent.removeExtra(START_IN_RECENTS_SCREEN) moveTaskToBack(true) } + + captureSnapshotTelemetryMetrics() } @CallSuper @@ -523,6 +528,23 @@ open class HomeActivity : LocaleAwareAppCompatActivity() { this.visualCompletenessQueue = visualCompletenessQueue } + private fun captureSnapshotTelemetryMetrics() = CoroutineScope(Dispatchers.IO).launch { + // PWA + val recentlyUsedPwaCount = components.core.webAppShortcutManager.recentlyUsedWebAppsCount( + activeThresholdMs = PWA_RECENTLY_USED_THRESHOLD + ) + if (recentlyUsedPwaCount == 0) { + Metrics.hasRecentPwas.set(false) + } else { + Metrics.hasRecentPwas.set(true) + // This metric's lifecycle is set to 'application', meaning that it gets reset upon + // application restart. Combined with the behaviour of the metric type itself (a growing counter), + // it's important that this metric is only set once per application's lifetime. + // Otherwise, we're going to over-count. + Metrics.recentlyUsedPwaCount.add(recentlyUsedPwaCount) + } + } + @VisibleForTesting internal fun isActivityColdStarted(startingIntent: Intent, activityIcicle: Bundle?): Boolean = // First time opening this activity in the task. @@ -541,5 +563,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity() { const val EXTRA_OPENED_FROM_NOTIFICATION = "notification_open" const val delay = 5000L const val START_IN_RECENTS_SCREEN = "start_in_recents_screen" + + // PWA must have been used within last 30 days to be considered "recently used" for the + // telemetry purposes. + const val PWA_RECENTLY_USED_THRESHOLD = DateUtils.DAY_IN_MILLIS * 30L } } diff --git a/docs/metrics.md b/docs/metrics.md index 69aa677cf..68ebc7c3a 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -251,8 +251,10 @@ The following metrics are added to the ping: | metrics.adjust_network |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |A string containing the Adjust network ID from which the user installed Fenix. This will not send on the first session the user runs. If the install is organic, this will be empty. |[1](https://github.com/mozilla-mobile/fenix/pull/9253)||2020-09-01 | | metrics.default_browser |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Is Fenix the default browser? |[1](https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673)||2020-09-01 | | metrics.default_moz_browser |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The name of the default browser on device if and only if it's a Mozilla owned product |[1](https://github.com/mozilla-mobile/fenix/pull/1953/), [2](https://github.com/mozilla-mobile/fenix/pull/5216)||2020-09-01 | +| metrics.has_recent_pwas |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |A boolean that indicates if the user has recently used PWAs. See recently_used_pwa_count for the actual count. |[1](https://github.com/mozilla-mobile/fenix/pull/11982#pullrequestreview-437963817)||2020-12-01 | | metrics.has_top_sites |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |A boolean that indicates if the user has top sites |[1](https://github.com/mozilla-mobile/fenix/pull/9556)||2020-09-01 | | metrics.mozilla_products |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |A list of all the Mozilla products installed on device. We currently scan for: Firefox, Firefox Beta, Firefox Aurora, Firefox Nightly, Firefox Fdroid, Firefox Lite, Reference Browser, Reference Browser Debug, Fenix, Focus, and Lockwise. |[1](https://github.com/mozilla-mobile/fenix/pull/1953/), [2](https://github.com/mozilla-mobile/fenix/pull/5216)||2020-09-01 | +| metrics.recently_used_pwa_count |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |A counter that indicates how many PWAs a user has recently used. Threshold for "recency" set in HomeActivity#PWA_RECENTLY_USED_THRESHOLD. Currently we are not told by the OS when a PWA is removed by the user, so we use the "recently used" heuristic to judge how many PWAs are still active, as a proxy for "installed". This value will only be set if the user has at least *one* recently used PWA. If they have 0, this metric will not be sent, resulting in a null value during analysis on the server-side. To disambiguate between a failed `recently_used_pwa_count` metric and 0 recent PWAs, please see `has_recent_pwas`. |[1](https://github.com/mozilla-mobile/fenix/pull/11982#pullrequestreview-437963817)||2020-12-01 | | metrics.search_count |[labeled_counter](https://mozilla.github.io/glean/book/user/metrics/labeled_counters.html) |The labels for this counter are `.`. If the search engine is bundled with Fenix `search-engine-name` will be the name of the search engine. If it's a custom search engine (defined: https://github.com/mozilla-mobile/fenix/issues/1607) the value will be `custom`. `source` will be: `action`, `suggestion`, `widget` or `shortcut` (depending on the source from which the search started). Also added the `other` option for the source but it should never enter on this case. |[1](https://github.com/mozilla-mobile/fenix/pull/1677), [2](https://github.com/mozilla-mobile/fenix/pull/5216), [3](https://github.com/mozilla-mobile/fenix/pull/7310)||2020-09-01 | | metrics.search_widget_installed |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the search widget is installed |[1](https://github.com/mozilla-mobile/fenix/pull/10958)||2020-09-01 | | metrics.toolbar_position |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |A string that indicates the new position of the toolbar TOP or BOTTOM |[1](https://github.com/mozilla-mobile/fenix/pull/6608)||2020-09-01 | From e6a56aec36fba68ebed183c3c268126ece5135df Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Fri, 26 Jun 2020 17:51:18 -0400 Subject: [PATCH 17/17] For #12026 - Update default top sites for new users (#12028) --- .../java/org/mozilla/fenix/ui/HomeScreenTest.kt | 2 +- .../java/org/mozilla/fenix/ui/TopSitesTest.kt | 2 +- .../org/mozilla/fenix/components/TopSiteStorage.kt | 14 +++++++------- .../org/mozilla/fenix/settings/SupportUtils.kt | 2 +- app/src/main/res/values/static_strings.xml | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt index 9d87b2beb..8113ad30e 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HomeScreenTest.kt @@ -50,8 +50,8 @@ class HomeScreenTest { // Verify Top Sites verifyExistingTopSitesList() verifyExistingTopSitesTabs("Wikipedia") - verifyExistingTopSitesTabs("YouTube") verifyExistingTopSitesTabs("Top Articles") + verifyExistingTopSitesTabs("Google") } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt index 2e062ca7e..2081886f1 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt @@ -148,7 +148,7 @@ class TopSitesTest { val defaultTopSites = arrayOf( "Top Articles", "Wikipedia", - "YouTube" + "Google" ) homeScreen { }.dismissOnboarding() diff --git a/app/src/main/java/org/mozilla/fenix/components/TopSiteStorage.kt b/app/src/main/java/org/mozilla/fenix/components/TopSiteStorage.kt index a9ddaacee..c7c0347bd 100644 --- a/app/src/main/java/org/mozilla/fenix/components/TopSiteStorage.kt +++ b/app/src/main/java/org/mozilla/fenix/components/TopSiteStorage.kt @@ -56,6 +56,13 @@ class TopSiteStorage(private val context: Context) { private fun addDefaultTopSites() { val topSiteCandidates = mutableListOf>() if (!context.settings().defaultTopSitesAdded) { + topSiteCandidates.add( + Pair( + context.getString(R.string.default_top_site_google), + SupportUtils.GOOGLE_URL + ) + ) + if (LocaleManager.getSelectedLocale(context).language == "en") { topSiteCandidates.add( Pair( @@ -72,13 +79,6 @@ class TopSiteStorage(private val context: Context) { ) ) - topSiteCandidates.add( - Pair( - context.getString(R.string.default_top_site_youtube), - SupportUtils.YOUTUBE_URL - ) - ) - GlobalScope.launch(Dispatchers.IO) { topSiteCandidates.forEach { (title, url) -> addTopSite(title, url, isDefault = true) diff --git a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt index 372ac00de..a49f6668a 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt @@ -26,7 +26,7 @@ object SupportUtils { const val FENIX_PLAY_STORE_URL = "https://play.google.com/store/apps/details?id=${BuildConfig.APPLICATION_ID}" const val FIREFOX_BETA_PLAY_STORE_URL = "market://details?id=org.mozilla.firefox_beta" const val FIREFOX_NIGHTLY_PLAY_STORE_URL = "market://details?id=org.mozilla.fennec_aurora" - const val YOUTUBE_URL = "https://www.youtube.com/" + const val GOOGLE_URL = "https://www.google.com/" enum class SumoTopic(internal val topicStr: String) { FENIX_MOVING("sync-delist"), diff --git a/app/src/main/res/values/static_strings.xml b/app/src/main/res/values/static_strings.xml index f2f2ff8c7..fe7ea6b8f 100644 --- a/app/src/main/res/values/static_strings.xml +++ b/app/src/main/res/values/static_strings.xml @@ -23,8 +23,8 @@ Top Articles Wikipedia - - YouTube + + Google AC