From 358ca2c87f7ca78730b5f0d0b5dbd1a7782954df Mon Sep 17 00:00:00 2001 From: Mugurell Date: Thu, 22 Oct 2020 02:21:37 +0300 Subject: [PATCH 001/199] For #15543 - Adjust the height of the tabs tray depending on the number of tabs (#15749) --- .../fenix/tabtray/TabTrayDialogFragment.kt | 10 ++-- .../org/mozilla/fenix/tabtray/TabTrayView.kt | 55 ++++++++++++++----- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt index 849ac9e64..dd72cf8b4 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt @@ -161,14 +161,13 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler if (newConfig.orientation != currentOrientation) { tabTrayView.dismissMenu() - tabTrayView.expand() + tabTrayView.updateBottomSheetBehavior() if (requireContext().settings().gridTabView) { // Update the number of columns to use in the grid view when the screen // orientation changes. tabTrayView.updateTabsTrayLayout() } - currentOrientation = newConfig.orientation } } @@ -205,8 +204,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler ), store = tabTrayDialogStore, isPrivate = isPrivate, - startingInLandscape = requireContext().resources.configuration.orientation == - Configuration.ORIENTATION_LANDSCAPE, + isInLandscape = ::isInLandscape, lifecycleOwner = viewLifecycleOwner ) { private -> val filter: (TabSessionState) -> Boolean = { state -> private == state.content.private } @@ -450,6 +448,10 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler } } + private fun isInLandscape(): Boolean { + return requireContext().resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + } + companion object { private const val ELEVATION = 80f } diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt index 25eaad1cf..87894865a 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt @@ -40,6 +40,7 @@ import mozilla.components.browser.state.selector.getNormalOrPrivateTabs import mozilla.components.browser.state.selector.normalTabs import mozilla.components.browser.state.selector.privateTabs import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.tabstray.TabViewHolder import mozilla.components.feature.syncedtabs.SyncedTabsFeature import mozilla.components.support.base.feature.ViewBoundFeatureWrapper @@ -54,6 +55,7 @@ import org.mozilla.fenix.ext.settings import org.mozilla.fenix.tabtray.SaveToCollectionsButtonAdapter.MultiselectModeChange import org.mozilla.fenix.tabtray.TabTrayDialogFragmentState.Mode import java.text.NumberFormat +import kotlin.math.max import mozilla.components.browser.storage.sync.Tab as SyncTab /** @@ -66,7 +68,7 @@ class TabTrayView( private val interactor: TabTrayInteractor, store: TabTrayDialogFragmentStore, isPrivate: Boolean, - startingInLandscape: Boolean, + private val isInLandscape: () -> Boolean, lifecycleOwner: LifecycleOwner, private val filterTabs: (Boolean) -> Unit ) : LayoutContainer, TabLayout.OnTabSelectedListener { @@ -145,20 +147,14 @@ class TabTrayView( view.tab_layout.addOnTabSelectedListener(this) - val tabs = if (isPrivate) { - view.context.components.core.store.state.privateTabs - } else { - view.context.components.core.store.state.normalTabs - } + val tabs = getTabs(isPrivate) val selectedBrowserTabIndex = tabs .indexOfFirst { it.id == view.context.components.core.store.state.selectedTabId } - if (tabs.size > EXPAND_AT_SIZE || startingInLandscape) { - expand() - } + updateBottomSheetBehavior() - setTopOffset(startingInLandscape) + setTopOffset(isInLandscape()) if (view.context.settings().syncedTabsInTabsTray) { syncedTabsFeature.set( @@ -280,6 +276,27 @@ class TabTrayView( } } + private fun getTabs(isPrivate: Boolean): List = if (isPrivate) { + view.context.components.core.store.state.privateTabs + } else { + view.context.components.core.store.state.normalTabs + } + + private fun getTabsNumberInAnyMode(): Int { + return max( + view.context.components.core.store.state.normalTabs.size, + view.context.components.core.store.state.privateTabs.size + ) + } + + private fun getTabsNumberForExpandingTray(): Int { + return if (container.context.settings().gridTabView) { + EXPAND_AT_GRID_SIZE + } else { + EXPAND_AT_LIST_SIZE + } + } + private fun handleTabClicked(tab: SyncTab) { interactor.onSyncedTabClicked(tab) } @@ -312,8 +329,17 @@ class TabTrayView( components.analytics.metrics.track(eventToSend) } - fun expand() { - behavior.state = BottomSheetBehavior.STATE_EXPANDED + /** + * Updates the bottom sheet height based on the number tabs or screen orientation. + * Show the bottom sheet fully expanded if it is in landscape mode or the number of + * tabs are greater or equal to the expand size limit. + */ + fun updateBottomSheetBehavior() { + if (isInLandscape() || getTabsNumberInAnyMode() >= getTabsNumberForExpandingTray()) { + behavior.state = BottomSheetBehavior.STATE_EXPANDED + } else { + behavior.state = BottomSheetBehavior.STATE_COLLAPSED + } } enum class TabChange { @@ -668,7 +694,10 @@ class TabTrayView( private const val TAB_COUNT_SHOW_CFR = 6 private const val DEFAULT_TAB_ID = 0 private const val PRIVATE_TAB_ID = 1 - private const val EXPAND_AT_SIZE = 3 + // Minimum number of list items for which to show the tabs tray as expanded. + private const val EXPAND_AT_LIST_SIZE = 4 + // Minimum number of grid items for which to show the tabs tray as expanded. + private const val EXPAND_AT_GRID_SIZE = 3 private const val SLIDE_OFFSET = 0 private const val SELECTION_DELAY = 500 private const val NORMAL_HANDLE_PERCENT_WIDTH = 0.1F From c40b93715fc890d006933d0585ffd07618fe2c4c Mon Sep 17 00:00:00 2001 From: mozilla-l10n-automation-bot <54512241+mozilla-l10n-automation-bot@users.noreply.github.com> Date: Wed, 21 Oct 2020 21:01:50 -0400 Subject: [PATCH 002/199] Import l10n. (#16108) --- app/src/main/res/values-zh-rCN/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index efdd21184..0c24796c6 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -61,8 +61,8 @@ 您已进入隐私浏览模式 - %1$s 在您关闭所有隐私标签页或退出应用后,将清除您的搜索记录与浏览历史。虽然这样做不会使您对网站或电信运营商匿名,但还是可以更简单地让使用此设备的人无法得知您在网上的活动。 - 隐私浏览功能的常见误解 + %1$s 会在关闭所有隐私标签页或退出应用时,清除您的搜索记录与浏览历史。虽然这样仍无法对网站和电信运营商匿名,但还是可以更简单地防止其他使用此设备的人得知您的上网活动。 + 正确认识隐私浏览功能 删除浏览会话 @@ -1167,7 +1167,7 @@ 标准(默认) - 少拦截一些跟踪器,页面将正可常加载。 + 少拦截一些跟踪器,页面将可正常加载。 严格(推荐) From d9c44f761f14ae6ffcb7db3be87ba16ca07e1475 Mon Sep 17 00:00:00 2001 From: Christian Sadilek Date: Thu, 22 Oct 2020 14:41:08 -0400 Subject: [PATCH 003/199] Update Android Components version to 64.0.20201022151526 --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 9a50ac774..6bac56fc9 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 = "63.0.20201019143159" + const val VERSION = "64.0.20201022151526" } From f514f6099aceac8a4a4c3a924ca871b98a205a50 Mon Sep 17 00:00:00 2001 From: Jocelyne Abi Haidar <38375996+joc-a@users.noreply.github.com> Date: Fri, 23 Oct 2020 00:22:37 +0300 Subject: [PATCH 004/199] For #15508: Show error when trying to save empty or invalid bookmark URL (#15674) --- .../bookmarks/edit/EditBookmarkFragment.kt | 36 +++++++++++++++++-- .../mozilla/fenix/utils/ClearableEditText.kt | 4 +++ .../res/layout/fragment_edit_bookmark.xml | 31 +++++++++------- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt index db2e3f71b..d6d3464ed 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt @@ -5,7 +5,10 @@ package org.mozilla.fenix.library.bookmarks.edit import android.content.DialogInterface +import android.content.res.ColorStateList import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher import android.view.Menu import android.view.MenuInflater import android.view.MenuItem @@ -13,6 +16,7 @@ import android.view.View import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.ViewModelProvider @@ -134,6 +138,23 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) { placeCursorAtEnd() showKeyboard() } + + view.bookmarkUrlEdit.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + // NOOP + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + bookmarkUrlEdit.onTextChanged(s) + + inputLayoutBookmarkUrl.error = null + inputLayoutBookmarkUrl.errorIconDrawable = null + } + + override fun afterTextChanged(s: Editable?) { + // NOOP + } + }) } } @@ -245,13 +266,24 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) { ) ) } + withContext(Main) { + inputLayoutBookmarkUrl.error = null + inputLayoutBookmarkUrl.errorIconDrawable = null + + findNavController().popBackStack() + } } catch (e: UrlParseFailed) { withContext(Main) { - bookmarkUrlEdit.error = getString(R.string.bookmark_invalid_url_error) + inputLayoutBookmarkUrl.error = getString(R.string.bookmark_invalid_url_error) + inputLayoutBookmarkUrl.setErrorIconDrawable(R.drawable.mozac_ic_warning_with_bottom_padding) + inputLayoutBookmarkUrl.setErrorIconTintList( + ColorStateList.valueOf( + ContextCompat.getColor(requireContext(), R.color.design_error) + ) + ) } } } progress_bar_bookmark.visibility = View.INVISIBLE - findNavController().popBackStack() } } diff --git a/app/src/main/java/org/mozilla/fenix/utils/ClearableEditText.kt b/app/src/main/java/org/mozilla/fenix/utils/ClearableEditText.kt index e2e7464bd..9c5212504 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/ClearableEditText.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/ClearableEditText.kt @@ -46,6 +46,10 @@ class ClearableEditText @JvmOverloads constructor( * Displays a clear icon if text has been entered. */ override fun onTextChanged(text: CharSequence?, start: Int, lengthBefore: Int, lengthAfter: Int) { + onTextChanged(text) + } + + fun onTextChanged(text: CharSequence?) { // lengthAfter has inconsistent behaviour when there are spaces in the entered text, so we'll use text.length. val textLength = text?.length ?: 0 val drawable = if (shouldShowClearButton(textLength)) { diff --git a/app/src/main/res/layout/fragment_edit_bookmark.xml b/app/src/main/res/layout/fragment_edit_bookmark.xml index 8d8a1df9e..ec0683d78 100644 --- a/app/src/main/res/layout/fragment_edit_bookmark.xml +++ b/app/src/main/res/layout/fragment_edit_bookmark.xml @@ -59,19 +59,26 @@ android:textColor="?primaryText" android:textSize="12sp" /> - + android:layout_height="wrap_content"> + + + + Date: Thu, 22 Oct 2020 23:12:05 -0400 Subject: [PATCH 005/199] Import l10n. (#16138) --- app/src/main/res/values-da/strings.xml | 6 ++++++ app/src/main/res/values-fy-rNL/strings.xml | 7 +++++++ app/src/main/res/values-gn/strings.xml | 7 +++++++ 3 files changed, 20 insertions(+) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 24d98df24..1b1e07a0b 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -212,6 +212,9 @@ Læs mere + + + Åbn et nyt Firefox-faneblad Søg @@ -1606,6 +1609,9 @@ The first parameter is the name of the app (e.g. Firefox Preview) --> Få det meste ud af %s. + + Tryk for at se flere detaljer + Saml de ting, der betyder noget for dig diff --git a/app/src/main/res/values-fy-rNL/strings.xml b/app/src/main/res/values-fy-rNL/strings.xml index 2e9c7824a..0c4138680 100644 --- a/app/src/main/res/values-fy-rNL/strings.xml +++ b/app/src/main/res/values-fy-rNL/strings.xml @@ -217,6 +217,8 @@ Mear ynfo + + In nij Firefox-ljepblêd iepenje Sykje @@ -627,6 +629,8 @@ Ljepblêden iepenje + + Namme kolleksje Fuortsmite @@ -1617,6 +1621,9 @@ The first parameter is the name of the app (e.g. Firefox Preview) --> Helje it measte út %s. + + Klik foar mear details + Sammelje de dingen dy\'t wichtich foar jo binne diff --git a/app/src/main/res/values-gn/strings.xml b/app/src/main/res/values-gn/strings.xml index 1d48306a0..d57aebbb8 100644 --- a/app/src/main/res/values-gn/strings.xml +++ b/app/src/main/res/values-gn/strings.xml @@ -219,6 +219,8 @@ Kuaave + + Embojuruja Firefox rendayke pyahu Heka @@ -635,6 +637,8 @@ Emoambue atygua réra Embojuruja tendayke + + Ñembyatyha réra Mboguete @@ -1646,6 +1650,9 @@ The first parameter is the name of the app (e.g. Firefox Preview) --> Eguenohẽ %s-gui eikotevẽva. + + Eikutu ápe eikuaave hag̃ua + Embyaty umi mba’ekuéra ehayhu añetéva From e15f50712af2d84d2ffe300da5d27dc92634744c Mon Sep 17 00:00:00 2001 From: ekager Date: Tue, 20 Oct 2020 12:37:43 -0700 Subject: [PATCH 006/199] For #6313 - Set session ClearColor through Engine DefaultSettings --- .../java/org/mozilla/fenix/FeatureFlags.kt | 5 --- .../fenix/browser/BaseBrowserFragment.kt | 35 +------------------ .../mozilla/fenix/browser/BrowserAnimator.kt | 18 ++-------- .../java/org/mozilla/fenix/components/Core.kt | 12 +++++-- .../fenix/settings/SecretSettingsFragment.kt | 6 ---- .../java/org/mozilla/fenix/utils/Settings.kt | 6 ---- app/src/main/res/layout/fragment_browser.xml | 1 - app/src/main/res/values/preference_keys.xml | 2 -- app/src/main/res/values/static_strings.xml | 2 -- .../res/xml/secret_settings_preferences.xml | 5 --- 10 files changed, 13 insertions(+), 79 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index 4cbe1ac30..b0a6d9cc6 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -26,11 +26,6 @@ object FeatureFlags { */ val showGridViewInTabsSettings = Config.channel.isNightlyOrDebug - /** - * Enables wait til first contentful paint - */ - val waitUntilPaintToDraw = Config.channel.isNightlyOrDebug - /** * Enables downloads with external download managers. */ diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt index 17997e65a..5323c35f0 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -247,9 +247,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session fragment = WeakReference(this), engineView = WeakReference(engineView), swipeRefresh = WeakReference(swipeRefresh), - viewLifecycleScope = WeakReference(viewLifecycleOwner.lifecycleScope), - settings = context.components.settings, - firstContentfulHappened = ::didFirstContentfulHappen + viewLifecycleScope = WeakReference(viewLifecycleOwner.lifecycleScope) ).apply { beginAnimateInIfNecessary() } @@ -659,28 +657,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session .collect { tab -> pipModeChanged(tab) } } - if (context.settings().waitToShowPageUntilFirstPaint) { - store.flowScoped(viewLifecycleOwner) { flow -> - flow.mapNotNull { state -> - state.findTabOrCustomTabOrSelectedTab( - customTabSessionId - ) - } - .ifChanged { it.content.firstContentfulPaint } - .collect { - val showEngineView = - it.content.firstContentfulPaint || it.content.progress == LOADING_PROGRESS_COMPLETE - - if (showEngineView) { - engineView?.asView()?.isVisible = true - swipeRefresh?.alpha = 1f - } else { - engineView?.asView()?.isVisible = false - } - } - } - } - view.swipeRefresh.isEnabled = FeatureFlags.pullToRefreshEnabled && context.settings().isPullToRefreshEnabledInBrowser if (view.swipeRefresh.isEnabled) { @@ -1153,15 +1129,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session } } - private fun didFirstContentfulHappen() = - if (components.settings.waitToShowPageUntilFirstPaint) { - val tab = - components.core.store.state.findTabOrCustomTabOrSelectedTab(customTabSessionId) - tab?.content?.firstContentfulPaint ?: false - } else { - true - } - /* * Dereference these views when the fragment view is destroyed to prevent memory leaks */ diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserAnimator.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserAnimator.kt index fcaaadde1..31827f236 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserAnimator.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserAnimator.kt @@ -18,7 +18,6 @@ import mozilla.components.concept.engine.EngineView import org.mozilla.fenix.R import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.utils.Settings import java.lang.ref.WeakReference /** @@ -29,9 +28,7 @@ class BrowserAnimator( private val fragment: WeakReference, private val engineView: WeakReference, private val swipeRefresh: WeakReference, - private val viewLifecycleScope: WeakReference, - private val settings: Settings, - private val firstContentfulHappened: () -> Boolean + private val viewLifecycleScope: WeakReference ) { private val unwrappedEngineView: EngineView? @@ -41,17 +38,8 @@ class BrowserAnimator( get() = swipeRefresh.get() fun beginAnimateInIfNecessary() { - if (settings.waitToShowPageUntilFirstPaint) { - if (firstContentfulHappened()) { - viewLifecycleScope.get()?.launch { - delay(ANIMATION_DELAY) - unwrappedEngineView?.asView()?.visibility = View.VISIBLE - unwrappedSwipeRefresh?.background = null - unwrappedSwipeRefresh?.alpha = 1f - } - } - } else { - unwrappedSwipeRefresh?.alpha = 1f + viewLifecycleScope.get()?.launch { + delay(ANIMATION_DELAY) unwrappedEngineView?.asView()?.visibility = View.VISIBLE unwrappedSwipeRefresh?.background = null } diff --git a/app/src/main/java/org/mozilla/fenix/components/Core.kt b/app/src/main/java/org/mozilla/fenix/components/Core.kt index 692625567..d3db961b8 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Core.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Core.kt @@ -8,6 +8,7 @@ import GeckoProvider import android.content.Context import android.content.res.Configuration import android.os.StrictMode +import androidx.core.content.ContextCompat import io.sentry.Sentry import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope @@ -21,8 +22,8 @@ import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.engine.EngineMiddleware import mozilla.components.browser.session.storage.SessionStorage import mozilla.components.browser.session.undo.UndoMiddleware -import mozilla.components.browser.state.action.RestoreCompleteAction import mozilla.components.browser.state.action.RecentlyClosedAction +import mozilla.components.browser.state.action.RestoreCompleteAction import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.browser.storage.sync.PlacesBookmarksStorage @@ -101,7 +102,11 @@ class Core( fontInflationEnabled = context.settings().shouldUseAutoSize, suspendMediaWhenInactive = false, forceUserScalableContent = context.settings().forceEnableZoom, - loginAutofillEnabled = context.settings().shouldAutofillLogins + loginAutofillEnabled = context.settings().shouldAutofillLogins, + clearColor = ContextCompat.getColor( + context, + R.color.foundation_normal_theme + ) ) GeckoEngine( @@ -230,7 +235,8 @@ class Core( // Now that we have restored our previous state (if there's one) let's remove timed out tabs if (!context.settings().manuallyCloseTabs) { store.state.tabs.filter { - (System.currentTimeMillis() - it.lastAccess) > context.settings().getTabTimeout() + (System.currentTimeMillis() - it.lastAccess) > context.settings() + .getTabTimeout() }.forEach { val session = sessionManager.findSessionById(it.id) if (session != null) { diff --git a/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt index aa4c7503d..88e123291 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt @@ -31,12 +31,6 @@ class SecretSettingsFragment : PreferenceFragmentCompat() { onPreferenceChangeListener = SharedPreferenceUpdater() } - requirePreference(R.string.pref_key_wait_first_paint).apply { - isVisible = FeatureFlags.waitUntilPaintToDraw - isChecked = context.settings().waitToShowPageUntilFirstPaint - onPreferenceChangeListener = SharedPreferenceUpdater() - } - requirePreference(R.string.pref_key_synced_tabs_tabs_tray).apply { isVisible = FeatureFlags.syncedTabsInTabsTray isChecked = context.settings().syncedTabsInTabsTray diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index f1a50f454..d9fd08908 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -129,12 +129,6 @@ class Settings(private val appContext: Context) : PreferencesHolder { featureFlag = FeatureFlags.showGridViewInTabsSettings ) - var waitToShowPageUntilFirstPaint by featureFlagPreference( - appContext.getPreferenceKey(R.string.pref_key_wait_first_paint), - default = false, - featureFlag = FeatureFlags.waitUntilPaintToDraw - ) - var syncedTabsInTabsTray by featureFlagPreference( appContext.getPreferenceKey(R.string.pref_key_synced_tabs_tabs_tray), default = false, diff --git a/app/src/main/res/layout/fragment_browser.xml b/app/src/main/res/layout/fragment_browser.xml index 93fa18f61..63150e569 100644 --- a/app/src/main/res/layout/fragment_browser.xml +++ b/app/src/main/res/layout/fragment_browser.xml @@ -26,7 +26,6 @@ android:id="@+id/swipeRefresh" android:layout_width="match_parent" android:layout_height="match_parent" - android:alpha="0" app:layout_behavior="@string/appbar_scrolling_view_behavior"> pref_key_migrating_from_fenix_tip pref_key_master_password_tip - pref_key_wait_first_paint - pref_key_synced_tabs_tabs_tray pref_key_debug_settings diff --git a/app/src/main/res/values/static_strings.xml b/app/src/main/res/values/static_strings.xml index 51c58369f..6acb14828 100644 --- a/app/src/main/res/values/static_strings.xml +++ b/app/src/main/res/values/static_strings.xml @@ -34,8 +34,6 @@ Secret Settings Show Grid View in Tabs Settings - - Wait Until First Paint To Show Page Content Show Synced Tabs in the tabs tray diff --git a/app/src/main/res/xml/secret_settings_preferences.xml b/app/src/main/res/xml/secret_settings_preferences.xml index 8b53b5e22..a0c531b5b 100644 --- a/app/src/main/res/xml/secret_settings_preferences.xml +++ b/app/src/main/res/xml/secret_settings_preferences.xml @@ -9,11 +9,6 @@ android:key="@string/pref_key_show_grid_view_tabs_settings" android:title="@string/preferences_debug_settings_show_grid_view_tabs_settings" app:iconSpaceReserved="false" /> - Date: Fri, 23 Oct 2020 15:33:32 +0000 Subject: [PATCH 007/199] Update Android Components version to 64.0.20201023143136. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 6bac56fc9..11037ff4e 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 = "64.0.20201022151526" + const val VERSION = "64.0.20201023143136" } From 6353e9979e1c6cfe9e84172a3ada8a3b9e7fcf9e Mon Sep 17 00:00:00 2001 From: mozilla-l10n-automation-bot <54512241+mozilla-l10n-automation-bot@users.noreply.github.com> Date: Fri, 23 Oct 2020 22:18:34 -0400 Subject: [PATCH 008/199] Import l10n. (#16154) --- app/src/main/res/values-co/strings.xml | 5 +++++ app/src/main/res/values-su/strings.xml | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/app/src/main/res/values-co/strings.xml b/app/src/main/res/values-co/strings.xml index 094e4bdda..bfa5ec909 100644 --- a/app/src/main/res/values-co/strings.xml +++ b/app/src/main/res/values-co/strings.xml @@ -813,6 +813,8 @@ + + Allucamentu permanente Dumandà per permette @@ -1628,6 +1630,9 @@ The first parameter is the name of the app (e.g. Firefox Preview) --> Ottene u più bellu da %s. + + Cliccu per sapene di più + Racuglite ciò chì conta per voi diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml index cc139c87c..fd1bdd959 100644 --- a/app/src/main/res/values-su/strings.xml +++ b/app/src/main/res/values-su/strings.xml @@ -143,6 +143,8 @@ Pasang Tab singkron + + Singkronkeun deui Panggihan dina kaca @@ -212,6 +214,8 @@ Lenyepan + + Buka tab Firefox anyar Paluruh @@ -339,6 +343,11 @@ Iber + + Ngaran koléksi + + Pamilik koléksi (Sandiasma) + Singkronkeun ayeuna @@ -474,6 +483,11 @@ Gorolongkeun pikeun nyumputkeun tulbar + + Gilerkeun tulbar ka gigir pikeun pindah tab + + Gilerkeun tulbar ka luhur pikeun muka tab + Sesi From 7d59a58134a0d6fa4d987c6ab96bee941ebb8a8b Mon Sep 17 00:00:00 2001 From: mozilla-l10n-automation-bot <54512241+mozilla-l10n-automation-bot@users.noreply.github.com> Date: Mon, 26 Oct 2020 12:07:02 -0400 Subject: [PATCH 009/199] Import l10n. (#16163) --- app/src/main/res/values-ar/strings.xml | 6 ++++ app/src/main/res/values-ka/strings.xml | 16 +++++------ app/src/main/res/values-pt-rPT/strings.xml | 17 ++++++++++++ app/src/main/res/values-ta/strings.xml | 32 +++++++++++++--------- app/src/main/res/values-zh-rCN/strings.xml | 2 +- 5 files changed, 51 insertions(+), 22 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index c4f93eec6..8681e0d10 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1292,6 +1292,10 @@ ما جديد %s + + ‏%s | المكتبات مفتوحة المصدر + الدعم @@ -1466,6 +1470,8 @@ اطّلع على المزيد + + تفاصيل محرك البحث المخصّص رابط ”اطّلع على المزيد“ diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index d763cc2db..ef89d1f04 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -142,7 +142,7 @@ დაყენება - დასინქრონებული ჩანართები + დასინქრონებული კვლავ დასინქრონება @@ -328,9 +328,9 @@ ძიება დათვალიერების ისტორიაში - სანიშნების ძიება + ძიება სანიშნებში - დასინქრონებული ჩანართების მოძიება + დასინქრონებულ ჩანართებში ძიება ანგარიშის პარამეტრები @@ -464,7 +464,7 @@ - firefox.com/pair]]> + firefox.com/pair]]> ჩართეთ კამერა @@ -488,12 +488,12 @@ - ჩამოწიეთ გასაახლებლად + ჩამოწევით გაახლება - გადაადგილდით ხელსაწყოების დასამალად + გადაადგილებისას ხელსაწყოთა დამალვა - გაუსვით ხელსაწყოთა ზოლის გვერდებზე, ჩანართებზე გადასართველად + ხელსაწყოთა ზოლზე გასმით, ჩანართებზე გადასვლა აუსვით ხელსაწყოთა ზოლზე ჩანართების გახსნისთვის @@ -1503,7 +1503,7 @@ შეიყვანეთ საძიებო ფრაზა - შეამოწმეთ, შეესაბამება თუ არა საძიებო ფრაზა მოცემულ ფორმატს + დარწმუნდით, რომ შეესაბამება მოცემულ მაგალითს შეცდომა კავშირისას – „%s“ diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 1b37478e4..a62743df8 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -216,6 +216,8 @@ Saber mais + + Abrir um novo separador Firefox Pesquisar @@ -545,6 +547,14 @@ Sem separadores fechados recentemente + + Separadores + + Vista de separadores + + Lista + + Grelha Fechar separadores @@ -622,6 +632,8 @@ Renomear coleção Abrir separadores + + Nome da coleção Remover @@ -803,6 +815,8 @@ Notificação + + Armazenamento persistente Solicitar permissão @@ -1615,6 +1629,9 @@ The first parameter is the name of the app (e.g. Firefox Preview) --> Tire o máximo proveito do %s. + + Clique para mais detalhes + Colecione as coisas que são importantes para si diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index bdfa6684f..72ad17abf 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -214,7 +214,6 @@ மேலும் அறிய - தேடல் @@ -292,6 +291,8 @@ கருப்பொருள் முகப்பு + + சைகைகள் தனிப்பயனாக்கு @@ -469,6 +470,12 @@ சாதனக் கருப்பொருளைப் பின்பற்று + + + புதுப்பிக்க இழுங்கள் + + கருவிப்பட்டியை மறைக்க உருட்டுங்கள் + அமர்வுகள் @@ -515,7 +522,6 @@ சமீபத்தில் மூடிய கீற்றுகள் ஏதுமில்லை - கீற்றுகளை மூடு @@ -1059,10 +1065,6 @@ உங்கள் பயர்பாக்சு கணக்குடன் புத்தகக்குறிகள், கடவுச்சொற்கள், மற்றும் பலவற்றை ஒத்திசைக்கத் தொடங்குங்கள். மேலும் அறிக - - இந்தத் தொலைபேசியில் மற்றொரு பயர்பாக்சு உலாவியில் %s பெயரில் உள்நுழைந்துள்ளீர்கள். இக்கணக்கில் உள்நுழைய விரும்புகிறீர்களா? ஆம், புகுபதி @@ -1309,9 +1311,6 @@ குறுக்குவழி பெயர் - - விரைவான அணுகலுக்கும் மற்றும் செயலி போன்ற அனுபவத்துடனும் விரைவாக உலாவ நீங்கள் இந்த வலைப்பக்கத்தை உங்கள் முகப்பு திரையில் எளிதில் சேர்க்கலாம். - புகுபதிகைககளும் கடவுச்சொற்களும் @@ -1382,8 +1381,12 @@ தளம் ஒட்டுப்பலகைக்கு நகலெடுக்கப்பட்டது கடவுச்சொல்லை நகலெடு + + கடவுச்சொல்லை அழி பயனர் பெயரை நகலெடு + + பயனர்பெயரை அழி தளத்தை நகலெடு @@ -1537,7 +1540,7 @@ அந்தப் பயனர்பெயருடன் ஒரு புகுபதிகை ஏற்கனவே உள்ளது - + மற்றொரு சாதனத்தை இணைக்க. @@ -1557,8 +1560,6 @@ முதன்மை தள வரம்பை அடைந்தது - - புதிய முதன்மை தளத்தைச் சேர்க்க, ஒன்றை அகற்றுங்கள். தளத்தை நெடுநேரம் அழுத்தி அகற்று என்பதைத் தேர்ந்தெடுங்கள். சரி, புரிந்தது @@ -1568,7 +1569,7 @@ நீக்கு - %s இலிருந்து ஆக அதிகமானதைப் பெறுங்கள். @@ -1576,4 +1577,9 @@ உங்களுக்கு முக்கியமான விடயங்களைச் சேகரியுங்கள் விரைவான அணுகலுக்காக ஒத்த தேடல்கள், தளங்கள், கீற்றுகளை ஒன்றிணையுங்கள். + + இந்தத் தொலைபேசியில் மற்றொரு பயர்பாக்சு உலாவியில் %s பெயரில் உள்நுழைந்துள்ளீர்கள். இக்கணக்கில் உள்நுழைய விரும்புகிறீர்களா? + + விரைவான அணுகலுக்கும் மற்றும் செயலி போன்ற அனுபவத்துடனும் விரைவாக உலாவ நீங்கள் இந்த வலைப்பக்கத்தை உங்கள் முகப்பு திரையில் எளிதில் சேர்க்கலாம். + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 0c24796c6..af3a8a7f6 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1033,7 +1033,7 @@ - 中文字体测试:天地玄黄,宇宙洪荒。日月盈仄,辰宿列张。 + 这是示例文本。您可以增大或减小字体大小设置,调整文字显示效果。 使网站上的文字更大或更小 From a0f5fd686cb19df90a2b964bbf652d1d3b0aab6e Mon Sep 17 00:00:00 2001 From: Christian Sadilek Date: Mon, 26 Oct 2020 14:44:53 -0400 Subject: [PATCH 010/199] Update Android Components version to 64.0.20201026161129 (#16188) --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 11037ff4e..a95b7cd69 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 = "64.0.20201023143136" + const val VERSION = "64.0.20201026161129" } From f1cc9faed096ea66c82b05c2ca0d03b561a4d6aa Mon Sep 17 00:00:00 2001 From: ekager Date: Mon, 19 Oct 2020 17:21:16 -0700 Subject: [PATCH 011/199] No issue - Update lint detector tests to run --- .../fenix/lintrules/LicenseDetectorTest.kt | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/mozilla-lint-rules/src/test/java/org/mozilla/fenix/lintrules/LicenseDetectorTest.kt b/mozilla-lint-rules/src/test/java/org/mozilla/fenix/lintrules/LicenseDetectorTest.kt index 86c915a5c..b8daad6e6 100644 --- a/mozilla-lint-rules/src/test/java/org/mozilla/fenix/lintrules/LicenseDetectorTest.kt +++ b/mozilla-lint-rules/src/test/java/org/mozilla/fenix/lintrules/LicenseDetectorTest.kt @@ -4,14 +4,24 @@ package org.mozilla.fenix.lintrules +import com.android.tools.lint.checks.infrastructure.LintDetectorTest import com.android.tools.lint.checks.infrastructure.TestFiles -import com.android.tools.lint.checks.infrastructure.TestLintTask.lint +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 import org.mozilla.fenix.lintrules.LicenseCommentChecker.Companion.ValidLicenseForKotlinFiles import org.mozilla.fenix.lintrules.LicenseDetector.Companion.ISSUE_INVALID_LICENSE_FORMAT import org.mozilla.fenix.lintrules.LicenseDetector.Companion.ISSUE_MISSING_LICENSE -class LicenseDetectorTest { +@RunWith(JUnit4::class) +class LicenseDetectorTest : LintDetectorTest() { + + override fun getIssues(): MutableList = + mutableListOf(ISSUE_MISSING_LICENSE, ISSUE_INVALID_LICENSE_FORMAT) + + override fun getDetector(): Detector = LicenseDetector() @Test fun `report missing license if it isn't at the top of the file`() { @@ -41,7 +51,7 @@ class LicenseDetectorTest { lint() .files(TestFiles.kt(code)) - .issues(ISSUE_MISSING_LICENSE, ISSUE_INVALID_LICENSE_FORMAT) + .allowMissingSdk(true) .run() .expect(expectedReport) .expectFixDiffs(expectedFixOutput) @@ -73,7 +83,7 @@ class LicenseDetectorTest { lint() .files(TestFiles.kt(code)) - .issues(ISSUE_MISSING_LICENSE, ISSUE_INVALID_LICENSE_FORMAT) + .allowMissingSdk(true) .run() .expect(expectedReport) .expectFixDiffs(expectedFixOutput) @@ -95,7 +105,7 @@ class LicenseDetectorTest { lint() .files(TestFiles.kt(code)) - .issues(ISSUE_MISSING_LICENSE, ISSUE_INVALID_LICENSE_FORMAT) + .allowMissingSdk(true) .run() .expectClean() } @@ -132,7 +142,7 @@ class LicenseDetectorTest { lint() .files(TestFiles.kt(code)) - .issues(ISSUE_MISSING_LICENSE, ISSUE_INVALID_LICENSE_FORMAT) + .allowMissingSdk(true) .run() .expect(expectedReport) .expectFixDiffs(expectedFixOutput) From 90220678cf640a6d9f93afdb1ee721709e8873c3 Mon Sep 17 00:00:00 2001 From: Lorenzo Stanco Date: Mon, 26 Oct 2020 23:50:41 +0100 Subject: [PATCH 012/199] For #16122 - Add a dark theme color for the top sites pin icon (#16180) --- app/src/main/res/layout/top_site_item.xml | 3 ++- app/src/main/res/values-night/colors.xml | 1 + app/src/main/res/values/colors.xml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/top_site_item.xml b/app/src/main/res/layout/top_site_item.xml index 17be2c75d..ca01977b5 100644 --- a/app/src/main/res/layout/top_site_item.xml +++ b/app/src/main/res/layout/top_site_item.xml @@ -52,7 +52,8 @@ android:layout_width="16dp" android:layout_height="16dp" android:layout_gravity="center" - android:background="@drawable/ic_pin" /> + android:background="@drawable/ic_pin" + android:backgroundTint="@color/top_site_pin_icon_background_tint"/> diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 0190bf370..9f45796ae 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -77,6 +77,7 @@ @color/top_site_title_text_dark_theme #3A3944 #5B5B66 + @color/violet_50 @color/synced_tabs_separator_dark_theme diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 89963e642..9e9b5d157 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -281,6 +281,7 @@ @color/top_site_title_text_light_theme @color/photonLightGrey30 @color/light_grey_50 + @color/ink_20 @color/synced_tabs_separator_light_theme From b8d6dbd426bcfc8a6cbedbbbb18c2247bcf97fe4 Mon Sep 17 00:00:00 2001 From: mozilla-l10n-automation-bot <54512241+mozilla-l10n-automation-bot@users.noreply.github.com> Date: Mon, 26 Oct 2020 20:28:45 -0400 Subject: [PATCH 013/199] Import l10n. (#16198) --- app/src/main/res/values-sq/strings.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 31f1e5de3..0857b0ca2 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -213,6 +213,8 @@ Mësoni më tepër + + Hapni një skedë të re Firefox Kërko @@ -625,6 +627,8 @@ Hapi skedat + + Emër koleksioni Hiqe @@ -1619,6 +1623,9 @@ The first parameter is the name of the app (e.g. Firefox Preview) --> Përfitoni maksimumin nga %s. + + Klikoni për më tepër hollësi + Koleksiononi gjërat që kanë rëndësi për ju From b8fff8cef1bd784381a6c79792a313465adf8499 Mon Sep 17 00:00:00 2001 From: Aaron Train Date: Tue, 27 Oct 2020 10:19:24 -0400 Subject: [PATCH 014/199] For #8169 - Upgrade Mockwebserver (#16186) Upgrades Mockwebserver to 4.9.0 --- .../java/org/mozilla/fenix/glean/BaselinePingTest.kt | 2 +- .../java/org/mozilla/fenix/helpers/MockWebServer.kt | 12 +++++++----- .../mozilla/fenix/screenshots/MenuScreenShotTest.kt | 2 +- .../fenix/syncintegration/SyncIntegrationTest.kt | 2 +- .../java/org/mozilla/fenix/ui/BookmarksTest.kt | 2 +- .../java/org/mozilla/fenix/ui/ContextMenusTest.kt | 2 +- .../java/org/mozilla/fenix/ui/DeepLinkTest.kt | 2 +- .../java/org/mozilla/fenix/ui/DownloadTest.kt | 2 +- .../java/org/mozilla/fenix/ui/HistoryTest.kt | 2 +- .../org/mozilla/fenix/ui/MediaNotificationTest.kt | 2 +- .../org/mozilla/fenix/ui/NavigationToolbarTest.kt | 2 +- .../java/org/mozilla/fenix/ui/ReaderViewTest.kt | 2 +- .../java/org/mozilla/fenix/ui/SettingsAboutTest.kt | 2 +- .../java/org/mozilla/fenix/ui/SettingsAddonsTest.kt | 2 +- .../org/mozilla/fenix/ui/SettingsAdvancedTest.kt | 2 +- .../java/org/mozilla/fenix/ui/SettingsBasicsTest.kt | 2 +- .../mozilla/fenix/ui/SettingsDeveloperToolsTest.kt | 2 +- .../java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt | 2 +- .../java/org/mozilla/fenix/ui/SettingsSyncTest.kt | 2 +- .../java/org/mozilla/fenix/ui/SettingsTest.kt | 2 +- .../java/org/mozilla/fenix/ui/ShareButtonTest.kt | 2 +- .../java/org/mozilla/fenix/ui/SmokeTest.kt | 2 +- .../fenix/ui/StrictEnhancedTrackingProtectionTest.kt | 2 +- .../java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt | 2 +- .../org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt | 2 +- .../java/org/mozilla/fenix/ui/TopSitesTest.kt | 2 +- buildSrc/src/main/java/Dependencies.kt | 2 +- 27 files changed, 33 insertions(+), 31 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/glean/BaselinePingTest.kt b/app/src/androidTest/java/org/mozilla/fenix/glean/BaselinePingTest.kt index 1199adf35..afcc8d50e 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/glean/BaselinePingTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/glean/BaselinePingTest.kt @@ -110,7 +110,7 @@ class BaselinePingTest { do { attempts += 1 val request = server.takeRequest(20L, TimeUnit.SECONDS) ?: break - val docType = request.path.split("/")[3] + val docType = request.path!!.split("/")[3] if (pingName == docType) { val parsedPayload = JSONObject(request.getPlainBody()) if (pingReason == null) { diff --git a/app/src/androidTest/java/org/mozilla/fenix/helpers/MockWebServer.kt b/app/src/androidTest/java/org/mozilla/fenix/helpers/MockWebServer.kt index 5dfc98ec8..5bbc5f318 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/helpers/MockWebServer.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/MockWebServer.kt @@ -39,11 +39,13 @@ object MockWebServerHelper { */ fun createAlwaysOkMockWebServer(): MockWebServer { return MockWebServer().apply { - setDispatcher(object : Dispatcher() { + val dispatcher = object : Dispatcher() { + @Throws(InterruptedException::class) override fun dispatch(request: RecordedRequest): MockResponse { return MockResponse().setBody("OK") } - }) + } + this.dispatcher = dispatcher } } } @@ -62,10 +64,10 @@ const val HTTP_NOT_FOUND = 404 class AndroidAssetDispatcher : Dispatcher() { private val mainThreadHandler = Handler(Looper.getMainLooper()) - override fun dispatch(request: RecordedRequest?): MockResponse { + override fun dispatch(request: RecordedRequest): MockResponse { val assetManager = InstrumentationRegistry.getInstrumentation().context.assets try { - val pathWithoutQueryParams = Uri.parse(request?.path?.drop(1)).path + val pathWithoutQueryParams = Uri.parse(request.path!!.drop(1)).path assetManager.open(pathWithoutQueryParams!!).use { inputStream -> return fileToResponse(pathWithoutQueryParams, inputStream) } @@ -81,7 +83,7 @@ class AndroidAssetDispatcher : Dispatcher() { private fun fileToResponse(path: String, file: InputStream): MockResponse { return MockResponse() .setResponseCode(HTTP_OK) - .setBody(fileToBytes(file)) + .setBody(fileToBytes(file)!!) .addHeader("content-type: " + contentType(path)) } diff --git a/app/src/androidTest/java/org/mozilla/fenix/screenshots/MenuScreenShotTest.kt b/app/src/androidTest/java/org/mozilla/fenix/screenshots/MenuScreenShotTest.kt index c6e1e5951..3e3f2d674 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/screenshots/MenuScreenShotTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/screenshots/MenuScreenShotTest.kt @@ -43,7 +43,7 @@ class MenuScreenShotTest : ScreenshotTest() { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/syncintegration/SyncIntegrationTest.kt b/app/src/androidTest/java/org/mozilla/fenix/syncintegration/SyncIntegrationTest.kt index 283a46e05..7b6c02d16 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/syncintegration/SyncIntegrationTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/syncintegration/SyncIntegrationTest.kt @@ -44,7 +44,7 @@ class SyncIntegrationTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt index ddcdd7c84..59e08ac3b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt @@ -49,7 +49,7 @@ class BookmarksTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/ContextMenusTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/ContextMenusTest.kt index 67125a717..c877171ec 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/ContextMenusTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/ContextMenusTest.kt @@ -42,7 +42,7 @@ class ContextMenusTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/DeepLinkTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/DeepLinkTest.kt index 4f9a0885e..7bca26ebe 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/DeepLinkTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/DeepLinkTest.kt @@ -43,7 +43,7 @@ class DeepLinkTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/DownloadTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/DownloadTest.kt index cef6dbeb2..c0919cce8 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/DownloadTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/DownloadTest.kt @@ -51,7 +51,7 @@ class DownloadTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt index 19f97ddcb..fcca0e9de 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt @@ -41,7 +41,7 @@ class HistoryTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt index 5f3baff90..02ce669f2 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt @@ -37,7 +37,7 @@ class MediaNotificationTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt index 65e850860..ae9c165e6 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt @@ -38,7 +38,7 @@ class NavigationToolbarTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/ReaderViewTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/ReaderViewTest.kt index 4e08cbc41..e73ac0182 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/ReaderViewTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/ReaderViewTest.kt @@ -41,7 +41,7 @@ class ReaderViewTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt index 26368bd0a..ba101f0c9 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt @@ -33,7 +33,7 @@ class SettingsAboutTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt index 12b74d038..79057f88c 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt @@ -39,7 +39,7 @@ class SettingsAddonsTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAdvancedTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAdvancedTest.kt index 381cc9862..c12669080 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAdvancedTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAdvancedTest.kt @@ -32,7 +32,7 @@ class SettingsAdvancedTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt index 3e43e80b7..9efc25ab8 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt @@ -39,7 +39,7 @@ class SettingsBasicsTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsDeveloperToolsTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsDeveloperToolsTest.kt index f42902c99..c7cfed81f 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsDeveloperToolsTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsDeveloperToolsTest.kt @@ -33,7 +33,7 @@ class SettingsDeveloperToolsTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt index 78802cda9..0c2bbba58 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt @@ -41,7 +41,7 @@ class SettingsPrivacyTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsSyncTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsSyncTest.kt index 21ce9cf77..55bd79651 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsSyncTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsSyncTest.kt @@ -32,7 +32,7 @@ class SettingsSyncTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsTest.kt index ebb66370a..2672d1afd 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsTest.kt @@ -32,7 +32,7 @@ class SettingsTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/ShareButtonTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/ShareButtonTest.kt index f8f942035..bc9767cdb 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/ShareButtonTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/ShareButtonTest.kt @@ -33,7 +33,7 @@ class ShareButtonTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt index 695fac0d0..d4f64ef4b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt @@ -34,7 +34,7 @@ class SmokeTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt index 86f5c929f..659f7547e 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt @@ -42,7 +42,7 @@ class StrictEnhancedTrackingProtectionTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt index bebecaba5..a946cfb78 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt @@ -49,7 +49,7 @@ class TabbedBrowsingTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt index 59e392a06..0de072569 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt @@ -32,7 +32,7 @@ class ThreeDotMenuMainTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } 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 674e767c2..65cb25b59 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt @@ -36,7 +36,7 @@ class TopSitesTest { @Before fun setUp() { mockWebServer = MockWebServer().apply { - setDispatcher(AndroidAssetDispatcher()) + dispatcher = AndroidAssetDispatcher() start() } } diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index 67c256090..f30f5c691 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -43,7 +43,7 @@ object Versions { const val junit = "5.5.2" const val mockk = "1.10.0" - const val mockwebserver = "3.11.0" + const val mockwebserver = "4.9.0" const val uiautomator = "2.2.0" const val google_ads_id_version = "16.0.0" From 0ae0b89d659fc1310078a38dff71add3b9467dfa Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Mon, 26 Oct 2020 16:46:48 -0400 Subject: [PATCH 015/199] Fix add-on translation braking changes --- .../fenix/addons/AddonDetailsFragment.kt | 7 +- .../mozilla/fenix/addons/AddonDetailsView.kt | 4 +- .../addons/AddonInternalSettingsFragment.kt | 6 +- .../addons/AddonPermissionsDetailsFragment.kt | 6 +- .../fenix/addons/AddonsManagementFragment.kt | 16 ++-- .../addons/InstalledAddonDetailsFragment.kt | 88 +++++++++++-------- 6 files changed, 74 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/addons/AddonDetailsFragment.kt b/app/src/main/java/org/mozilla/fenix/addons/AddonDetailsFragment.kt index 38d9441ad..b391f5b69 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/AddonDetailsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/AddonDetailsFragment.kt @@ -16,7 +16,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import mozilla.components.feature.addons.Addon import mozilla.components.feature.addons.ui.showInformationDialog -import mozilla.components.feature.addons.ui.translatedName +import mozilla.components.feature.addons.ui.translateName import mozilla.components.feature.addons.update.DefaultAddonUpdater.UpdateAttemptStorage import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity @@ -34,7 +34,10 @@ class AddonDetailsFragment : Fragment(R.layout.fragment_add_on_details), AddonDe override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - showToolbar(title = args.addon.translatedName) + context?.let { + showToolbar(title = args.addon.translateName(it)) + } + AddonDetailsView(view, interactor = this).bind(args.addon) } diff --git a/app/src/main/java/org/mozilla/fenix/addons/AddonDetailsView.kt b/app/src/main/java/org/mozilla/fenix/addons/AddonDetailsView.kt index b65dfc680..5561941e0 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/AddonDetailsView.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/AddonDetailsView.kt @@ -16,7 +16,7 @@ import androidx.core.text.getSpans import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.fragment_add_on_details.* import mozilla.components.feature.addons.Addon -import mozilla.components.feature.addons.ui.translatedDescription +import mozilla.components.feature.addons.ui.translateDescription import mozilla.components.feature.addons.ui.updatedAtDate import org.mozilla.fenix.R import java.text.DateFormat @@ -100,7 +100,7 @@ class AddonDetailsView( } private fun bindDetails(addon: Addon) { - val detailsText = addon.translatedDescription + val detailsText = addon.translateDescription(containerView.context) val parsedText = detailsText.replace("\n", "
") val text = HtmlCompat.fromHtml(parsedText, HtmlCompat.FROM_HTML_MODE_COMPACT) diff --git a/app/src/main/java/org/mozilla/fenix/addons/AddonInternalSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/addons/AddonInternalSettingsFragment.kt index 7c170f2f7..94571480a 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/AddonInternalSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/AddonInternalSettingsFragment.kt @@ -11,7 +11,7 @@ import android.view.ViewGroup import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import kotlinx.android.synthetic.main.fragment_add_on_internal_settings.* -import mozilla.components.feature.addons.ui.translatedName +import mozilla.components.feature.addons.ui.translateName import org.mozilla.fenix.R import org.mozilla.fenix.ext.showToolbar @@ -33,7 +33,9 @@ class AddonInternalSettingsFragment : AddonPopupBaseFragment() { override fun onResume() { super.onResume() - showToolbar(args.addon.translatedName) + context?.let { + showToolbar(args.addon.translateName(it)) + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/org/mozilla/fenix/addons/AddonPermissionsDetailsFragment.kt b/app/src/main/java/org/mozilla/fenix/addons/AddonPermissionsDetailsFragment.kt index 88bf2b9a3..53c04615a 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/AddonPermissionsDetailsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/AddonPermissionsDetailsFragment.kt @@ -9,7 +9,7 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.navigation.fragment.navArgs -import mozilla.components.feature.addons.ui.translatedName +import mozilla.components.feature.addons.ui.translateName import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R @@ -25,7 +25,9 @@ class AddonPermissionsDetailsFragment : Fragment(R.layout.fragment_add_on_permis override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - showToolbar(args.addon.translatedName) + context?.let { + showToolbar(args.addon.translateName(it)) + } AddonPermissionsDetailsView(view, interactor = this).bind(args.addon) } diff --git a/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt b/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt index 25fa9fc40..b6f3b60ca 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt @@ -26,7 +26,7 @@ import mozilla.components.feature.addons.AddonManagerException import mozilla.components.feature.addons.ui.AddonInstallationDialogFragment import mozilla.components.feature.addons.ui.AddonsManagerAdapter import mozilla.components.feature.addons.ui.PermissionsDialogFragment -import mozilla.components.feature.addons.ui.translatedName +import mozilla.components.feature.addons.ui.translateName import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components @@ -230,13 +230,15 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management) // No need to display an error message if installation was cancelled by the user. if (e !is CancellationException) { val rootView = activity?.getRootView() ?: view - showSnackBar( - rootView, - getString( - R.string.mozac_feature_addons_failed_to_install, - addon.translatedName + context?.let { + showSnackBar( + rootView, + getString( + R.string.mozac_feature_addons_failed_to_install, + addon.translateName(it) + ) ) - ) + } } addonProgressOverlay?.visibility = View.GONE isInstallationInProgress = false diff --git a/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt b/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt index 49cf693d3..345db7f35 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt @@ -20,7 +20,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import mozilla.components.feature.addons.Addon import mozilla.components.feature.addons.AddonManagerException -import mozilla.components.feature.addons.ui.translatedName +import mozilla.components.feature.addons.ui.translateName import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.ext.components @@ -85,7 +85,7 @@ class InstalledAddonDetailsFragment : Fragment() { } private fun bindUI(view: View) { - val title = addon.translatedName + val title = addon.translateName(view.context) showToolbar(title) bindEnableSwitch(view) @@ -117,13 +117,15 @@ class InstalledAddonDetailsFragment : Fragment() { switch.setText(R.string.mozac_feature_addons_enabled) view.settings.isVisible = shouldSettingsBeVisible() view.remove_add_on.isEnabled = true - showSnackBar( - view, - getString( - R.string.mozac_feature_addons_successfully_enabled, - addon.translatedName + context?.let { + showSnackBar( + view, + getString( + R.string.mozac_feature_addons_successfully_enabled, + addon.translateName(it) + ) ) - ) + } } }, onError = { @@ -131,13 +133,15 @@ class InstalledAddonDetailsFragment : Fragment() { switch.isClickable = true view.remove_add_on.isEnabled = true switch.setState(addon.isEnabled()) - showSnackBar( - view, - getString( - R.string.mozac_feature_addons_failed_to_enable, - addon.translatedName + context?.let { + showSnackBar( + view, + getString( + R.string.mozac_feature_addons_failed_to_enable, + addon.translateName(it) + ) ) - ) + } } } ) @@ -152,13 +156,15 @@ class InstalledAddonDetailsFragment : Fragment() { privateBrowsingSwitch.isVisible = it.isEnabled() switch.setText(R.string.mozac_feature_addons_disabled) view.remove_add_on.isEnabled = true - showSnackBar( - view, - getString( - R.string.mozac_feature_addons_successfully_disabled, - addon.translatedName + context?.let { + showSnackBar( + view, + getString( + R.string.mozac_feature_addons_successfully_disabled, + addon.translateName(it) + ) ) - ) + } } }, onError = { @@ -167,13 +173,15 @@ class InstalledAddonDetailsFragment : Fragment() { privateBrowsingSwitch.isClickable = true view.remove_add_on.isEnabled = true switch.setState(addon.isEnabled()) - showSnackBar( - view, - getString( - R.string.mozac_feature_addons_failed_to_disable, - addon.translatedName + context?.let { + showSnackBar( + view, + getString( + R.string.mozac_feature_addons_failed_to_disable, + addon.translateName(it) + ) ) - ) + } } } ) @@ -263,26 +271,30 @@ class InstalledAddonDetailsFragment : Fragment() { onSuccess = { runIfFragmentIsAttached { setAllInteractiveViewsClickable(view, true) - showSnackBar( - view, - getString( - R.string.mozac_feature_addons_successfully_uninstalled, - addon.translatedName + context?.let { + showSnackBar( + view, + getString( + R.string.mozac_feature_addons_successfully_uninstalled, + addon.translateName(it) + ) ) - ) + } view.findNavController().popBackStack() } }, onError = { _, _ -> runIfFragmentIsAttached { setAllInteractiveViewsClickable(view, true) - showSnackBar( - view, - getString( - R.string.mozac_feature_addons_failed_to_uninstall, - addon.translatedName + context?.let { + showSnackBar( + view, + getString( + R.string.mozac_feature_addons_failed_to_uninstall, + addon.translateName(it) + ) ) - ) + } } } ) From 0953c61082a91667befbd8fbf42b8505920e549c Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Tue, 27 Oct 2020 10:40:51 -0400 Subject: [PATCH 016/199] Update Android Components version to 64.0.20201027134712. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index a95b7cd69..97ab58835 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 = "64.0.20201026161129" + const val VERSION = "64.0.20201027134712" } From 9b2162fca269e417e01afea23c846ca377a27b68 Mon Sep 17 00:00:00 2001 From: ekager Date: Mon, 26 Oct 2020 17:55:48 -0700 Subject: [PATCH 017/199] For #16199 - Don't pass BrowserToolbarView in onStart when we just need parent --- app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index ebe364c3f..383c91683 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -164,7 +164,7 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { navController = findNavController(), settings = settings, appLinksUseCases = context.components.useCases.appLinksUseCases, - container = browserToolbarView.view.parent as ViewGroup + container = browserLayout as ViewGroup ) session.register( openInAppOnboardingObserver!!, From 21a67de94768995306c0b704451b06aae3ce0325 Mon Sep 17 00:00:00 2001 From: mcarare Date: Mon, 26 Oct 2020 16:37:38 +0200 Subject: [PATCH 018/199] For #15379: Use proper url when sharing. --- .../fenix/browser/BaseBrowserFragment.kt | 3 +- .../toolbar/BrowserToolbarMenuController.kt | 18 +++++- ...DefaultBrowserToolbarMenuControllerTest.kt | 63 ++++++++++++++++--- 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt index 5323c35f0..797fc143d 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -316,7 +316,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session bookmarkTapped = { viewLifecycleOwner.lifecycleScope.launch { bookmarkTapped(it) } }, scope = viewLifecycleOwner.lifecycleScope, tabCollectionStorage = requireComponents.core.tabCollectionStorage, - topSitesStorage = requireComponents.core.topSitesStorage + topSitesStorage = requireComponents.core.topSitesStorage, + browserStore = store ) _browserInteractor = BrowserInteractor( diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt index 4fd69b08e..9c0734c37 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt @@ -17,6 +17,8 @@ import kotlinx.coroutines.launch import mozilla.appservices.places.BookmarkRoot import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager +import mozilla.components.browser.state.selector.findTab +import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.EngineSession.LoadUrlFlags import mozilla.components.concept.engine.prompt.ShareData import mozilla.components.feature.session.SessionFeature @@ -66,7 +68,8 @@ class DefaultBrowserToolbarMenuController( private val bookmarkTapped: (Session) -> Unit, private val scope: CoroutineScope, private val tabCollectionStorage: TabCollectionStorage, - private val topSitesStorage: DefaultTopSitesStorage + private val topSitesStorage: DefaultTopSitesStorage, + private val browserStore: BrowserStore ) : BrowserToolbarMenuController { private val currentSession @@ -184,7 +187,7 @@ class DefaultBrowserToolbarMenuController( val directions = NavGraphDirections.actionGlobalShareFragment( data = arrayOf( ShareData( - url = currentSession?.url, + url = getProperUrl(currentSession), title = currentSession?.title ) ), @@ -295,6 +298,17 @@ class DefaultBrowserToolbarMenuController( } } + private fun getProperUrl(currentSession: Session?): String? { + return currentSession?.id?.let { + val currentTab = browserStore.state.findTab(it) + if (currentTab?.readerState?.active == true) { + currentTab.readerState.activeUrl + } else { + currentSession.url + } + } + } + @Suppress("ComplexMethod") private fun trackToolbarItemInteraction(item: ToolbarMenu.Item) { val eventItem = when (item) { diff --git a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt index 89e1a295a..4ed326317 100644 --- a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt @@ -26,6 +26,10 @@ import kotlinx.coroutines.test.runBlockingTest import mozilla.appservices.places.BookmarkRoot import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.ReaderState +import mozilla.components.browser.state.state.createTab +import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.prompt.ShareData import mozilla.components.feature.search.SearchUseCases @@ -85,6 +89,7 @@ class DefaultBrowserToolbarMenuControllerTest { @MockK private lateinit var sessionFeatureWrapper: ViewBoundFeatureWrapper @RelaxedMockK private lateinit var sessionFeature: SessionFeature @RelaxedMockK private lateinit var topSitesStorage: DefaultTopSitesStorage + @RelaxedMockK private lateinit var browserStore: BrowserStore @Before fun setUp() { @@ -334,11 +339,19 @@ class DefaultBrowserToolbarMenuControllerTest { } @Test - fun handleToolbarSharePress() = runBlockingTest { + fun handleToolbarSharePressWithReaderModeInactive() = runBlockingTest { val item = ToolbarMenu.Item.Share - + val title = "Mozilla" + val readerUrl = "moz-extension://1234" + val readerTab = createTab( + url = readerUrl, + readerState = ReaderState(active = false, activeUrl = "https://1234.org"), + title = title + ) + browserStore = BrowserStore(BrowserState(tabs = listOf(readerTab), selectedTabId = readerTab.id)) + every { currentSession.id } returns readerTab.id + every { currentSession.title } returns title every { currentSession.url } returns "https://mozilla.org" - every { currentSession.title } returns "Mozilla" val controller = createController(scope = this) controller.handleToolbarItemInteraction(item) @@ -346,10 +359,43 @@ class DefaultBrowserToolbarMenuControllerTest { verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.SHARE)) } verify { navController.navigate( - directionsEq(NavGraphDirections.actionGlobalShareFragment( - data = arrayOf(ShareData(url = "https://mozilla.org", title = "Mozilla")), - showPage = true - )) + directionsEq( + NavGraphDirections.actionGlobalShareFragment( + data = arrayOf(ShareData(url = "https://mozilla.org", title = "Mozilla")), + showPage = true + ) + ) + ) + } + } + + @Test + fun handleToolbarSharePressWithReaderModeActive() = runBlockingTest { + val item = ToolbarMenu.Item.Share + val title = "Mozilla" + val readerUrl = "moz-extension://1234" + val readerTab = createTab( + url = readerUrl, + readerState = ReaderState(active = true, activeUrl = "https://mozilla.org"), + title = title + ) + browserStore = BrowserStore(BrowserState(tabs = listOf(readerTab), selectedTabId = readerTab.id)) + every { currentSession.id } returns readerTab.id + every { currentSession.title } returns title + every { currentSession.url } returns readerUrl + + val controller = createController(scope = this) + controller.handleToolbarItemInteraction(item) + + verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.SHARE)) } + verify { + navController.navigate( + directionsEq( + NavGraphDirections.actionGlobalShareFragment( + data = arrayOf(ShareData(url = "https://mozilla.org", title = "Mozilla")), + showPage = true + ) + ) ) } } @@ -491,7 +537,8 @@ class DefaultBrowserToolbarMenuControllerTest { readerModeController = readerModeController, sessionManager = sessionManager, sessionFeature = sessionFeatureWrapper, - topSitesStorage = topSitesStorage + topSitesStorage = topSitesStorage, + browserStore = browserStore ).apply { ioScope = scope } From 655a5cbf5439c162b6c221031dc7f2606eb0bc9f Mon Sep 17 00:00:00 2001 From: Grisha Kruglov Date: Tue, 13 Oct 2020 14:34:20 -0700 Subject: [PATCH 019/199] Closes #15816 - Attempt to handle 'allowInPrivate..' pref with detached context --- .../fenix/addons/AddonsManagementFragment.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt b/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt index b6f3b60ca..6ed254466 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt @@ -35,6 +35,7 @@ import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.theme.ThemeManager +import java.lang.ref.WeakReference import java.util.concurrent.CancellationException /** @@ -169,7 +170,15 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management) private fun showInstallationDialog(addon: Addon) { if (!isInstallationInProgress && !hasExistingAddonInstallationDialogFragment()) { requireComponents.analytics.metrics.track(Event.AddonInstalled(addon.id)) - val addonCollectionProvider = requireContext().components.addonCollectionProvider + val context = requireContext() + val addonCollectionProvider = context.components.addonCollectionProvider + + // Fragment may not be attached to the context anymore during onConfirmButtonClicked handling, + // but we still want to be able to process user selection of the 'allowInPrivateBrowsing' pref. + // This is a best-effort attempt to do so - retain a weak reference to the application context + // (to avoid a leak), which we attempt to use to access addonManager. + // See https://github.com/mozilla-mobile/fenix/issues/15816 + val weakApplicationContext: WeakReference = WeakReference(context) val dialog = AddonInstallationDialogFragment.newInstance( addon = addon, @@ -189,7 +198,7 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management) ), onConfirmButtonClicked = { _, allowInPrivateBrowsing -> if (allowInPrivateBrowsing) { - requireContext().components.addonManager.setAddonAllowedInPrivateBrowsing( + weakApplicationContext.get()?.components?.addonManager?.setAddonAllowedInPrivateBrowsing( addon, allowInPrivateBrowsing, onSuccess = { From 601aa19176eba49e571cff417282a8dca9e14b41 Mon Sep 17 00:00:00 2001 From: Christian Sadilek Date: Tue, 20 Oct 2020 13:31:27 -0400 Subject: [PATCH 020/199] Closes #11285: Replace Session[Manager] observers in BaseBrowserFragment --- .../fenix/browser/BaseBrowserFragment.kt | 126 +++++++----- .../fenix/browser/BrowserFragmentTest.kt | 182 ++++++++++++++++++ 2 files changed, 258 insertions(+), 50 deletions(-) create mode 100644 app/src/test/java/org/mozilla/fenix/browser/BrowserFragmentTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt index 797fc143d..a5f7b5fcf 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -14,6 +14,7 @@ import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityManager import androidx.annotation.CallSuper +import androidx.annotation.VisibleForTesting import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.net.toUri import androidx.core.view.isVisible @@ -30,7 +31,6 @@ import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job -import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull @@ -38,12 +38,14 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import mozilla.appservices.places.BookmarkRoot import mozilla.components.browser.session.Session -import mozilla.components.browser.session.SessionManager import mozilla.components.browser.state.action.ContentAction import mozilla.components.browser.state.selector.findTab +import mozilla.components.browser.state.selector.findCustomTabOrSelectedTab import mozilla.components.browser.state.selector.findTabOrCustomTabOrSelectedTab import mozilla.components.browser.state.selector.getNormalOrPrivateTabs +import mozilla.components.browser.state.selector.selectedTab import mozilla.components.browser.state.state.SessionState +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.state.content.DownloadState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.browser.thumbnails.BrowserThumbnails @@ -69,6 +71,7 @@ import mozilla.components.feature.session.SwipeRefreshFeature import mozilla.components.feature.session.behavior.EngineViewBottomBehavior import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.feature.sitepermissions.SitePermissionsFeature +import mozilla.components.lib.state.ext.consumeFlow import mozilla.components.lib.state.ext.flowScoped import mozilla.components.service.sync.logins.DefaultLoginValidationDelegate import mozilla.components.support.base.feature.PermissionsFeature @@ -76,6 +79,7 @@ import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.ktx.android.view.exitImmersiveModeIfNeeded import mozilla.components.support.ktx.android.view.hideKeyboard +import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.HomeActivity @@ -126,7 +130,7 @@ import java.lang.ref.WeakReference */ @ExperimentalCoroutinesApi @Suppress("TooManyFunctions", "LargeClass") -abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, SessionManager.Observer, +abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, OnBackLongPressedListener, AccessibilityManager.AccessibilityStateChangeListener { private lateinit var browserFragmentStore: BrowserFragmentStore @@ -138,7 +142,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session get() = _browserInteractor!! private var _browserToolbarView: BrowserToolbarView? = null - protected val browserToolbarView: BrowserToolbarView + @VisibleForTesting + internal val browserToolbarView: BrowserToolbarView get() = _browserToolbarView!! protected val readerViewFeature = ViewBoundFeatureWrapper() @@ -165,7 +170,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session var customTabSessionId: String? = null - private var browserInitialized: Boolean = false + @VisibleForTesting + internal var browserInitialized: Boolean = false private var initUIJob: Job? = null protected var webAppToolbarShouldBeVisible = true @@ -226,6 +232,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session final override fun onViewCreated(view: View, savedInstanceState: Bundle?) { browserInitialized = initializeUI(view) != null + observeTabSelection(requireComponents.core.store) requireContext().accessibilityManager.addAccessibilityStateChangeListener(this) } @@ -235,7 +242,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session @Suppress("ComplexMethod", "LongMethod") @CallSuper - protected open fun initializeUI(view: View): Session? { + @VisibleForTesting + internal open fun initializeUI(view: View): Session? { val context = requireContext() val sessionManager = context.components.core.sessionManager val store = context.components.core.store @@ -252,7 +260,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session beginAnimateInIfNecessary() } - return getSessionById()?.also { session -> + return getSessionById()?.also { _ -> val openInFenixIntent = Intent(context, IntentReceiverActivity::class.java).apply { action = Intent.ACTION_VIEW putExtra(HomeActivity.OPEN_TO_BROWSER, true) @@ -629,28 +637,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session view = view ) - session.register(observer = object : Session.Observer { - override fun onUrlChanged(session: Session, url: String) { - browserToolbarView.expand() - } - - override fun onLoadRequest( - session: Session, - url: String, - triggeredByRedirect: Boolean, - triggeredByWebContent: Boolean - ) { - browserToolbarView.expand() - } - }, owner = viewLifecycleOwner) - - sessionManager.register(observer = object : SessionManager.Observer { - override fun onSessionSelected(session: Session) { - fullScreenChanged(false) - browserToolbarView.expand() - resumeDownloadDialogState(session.id, store, view, context, toolbarHeight) - } - }, owner = viewLifecycleOwner) + expandToolbarOnNavigation(store) store.flowScoped(viewLifecycleOwner) { flow -> flow.mapNotNull { state -> state.findTabOrCustomTabOrSelectedTab(customTabSessionId) } @@ -694,6 +681,21 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session } } + @VisibleForTesting + internal fun expandToolbarOnNavigation(store: BrowserStore) { + consumeFlow(store) { flow -> + flow.mapNotNull { + state -> state.findCustomTabOrSelectedTab(customTabSessionId) + } + .ifAnyChanged { + tab -> arrayOf(tab.content.url, tab.content.loadRequest) + } + .collect { + browserToolbarView.expand() + } + } + } + /** * Preserves current state of the [DynamicDownloadDialog] to persist through tab changes and * other fragments navigation. @@ -717,7 +719,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session * onTryAgain it will use [ContentAction.UpdateDownloadAction] to re-enqueue the former failed * download, because [DownloadsFeature] clears any queued downloads onStop. * */ - private fun resumeDownloadDialogState( + @VisibleForTesting + internal fun resumeDownloadDialogState( sessionId: String?, store: BrowserStore, view: View, @@ -810,26 +813,45 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session ): List @CallSuper - override fun onSessionSelected(session: Session) { - if (!this.isRemoving) { - updateThemeForSession(session) - } - if (!browserInitialized) { - // Initializing a new coroutineScope to avoid ConcurrentModificationException in ObserverRegistry - // This will be removed when ObserverRegistry is deprecated by browser-state. - initUIJob = MainScope().launch { - view?.let { - browserInitialized = initializeUI(it) != null - } + override fun onStart() { + super.onStart() + sitePermissionWifiIntegration.get()?.maybeAddWifiConnectedListener() + } + + @VisibleForTesting + internal fun observeTabSelection(store: BrowserStore) { + consumeFlow(store) { flow -> + flow.ifChanged { + it.selectedTabId + } + .mapNotNull { + it.selectedTab + } + .collect { + handleTabSelected(it) } } } - @CallSuper - override fun onStart() { - super.onStart() - requireComponents.core.sessionManager.register(this, this, autoPause = true) - sitePermissionWifiIntegration.get()?.maybeAddWifiConnectedListener() + private fun handleTabSelected(selectedTab: TabSessionState) { + if (!this.isRemoving) { + updateThemeForSession(selectedTab) + } + + if (browserInitialized) { + view?.let { view -> + fullScreenChanged(false) + browserToolbarView.expand() + + val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height) + val context = requireContext() + resumeDownloadDialogState(selectedTab.id, context.components.core.store, view, context, toolbarHeight) + } + } else { + view?.let { view -> + browserInitialized = initializeUI(view) != null + } + } } @CallSuper @@ -844,7 +866,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session } hideToolbar() - getSessionById()?.let { updateThemeForSession(it) } + components.core.store.state.findTabOrCustomTabOrSelectedTab(customTabSessionId)?.let { + updateThemeForSession(it) + } } @CallSuper @@ -1009,8 +1033,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session /** * Set the activity normal/private theme to match the current session. */ - private fun updateThemeForSession(session: Session) { - val sessionMode = BrowsingMode.fromBoolean(session.private) + @VisibleForTesting + internal fun updateThemeForSession(session: SessionState) { + val sessionMode = BrowsingMode.fromBoolean(session.content.private) (activity as HomeActivity).browsingModeManager.mode = sessionMode } @@ -1097,7 +1122,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session } } - private fun fullScreenChanged(inFullScreen: Boolean) { + @VisibleForTesting + internal fun fullScreenChanged(inFullScreen: Boolean) { if (inFullScreen) { // Close find in page bar if opened findInPageIntegration.onBackPressed() diff --git a/app/src/test/java/org/mozilla/fenix/browser/BrowserFragmentTest.kt b/app/src/test/java/org/mozilla/fenix/browser/BrowserFragmentTest.kt new file mode 100644 index 000000000..391ac3e65 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/browser/BrowserFragmentTest.kt @@ -0,0 +1,182 @@ +/* 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.browser + +import android.content.Context +import android.view.View +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +import io.mockk.every +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import mozilla.components.browser.state.action.ContentAction +import mozilla.components.browser.state.action.TabListAction +import mozilla.components.browser.state.state.LoadRequestState +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.browser.state.state.createTab +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.support.test.ext.joinBlocking +import mozilla.components.support.test.rule.MainCoroutineRule +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.FenixApplication +import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.components.toolbar.BrowserToolbarView +import org.mozilla.fenix.ext.application +import org.mozilla.fenix.ext.components +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner + +@ExperimentalCoroutinesApi +@RunWith(FenixRobolectricTestRunner::class) +class BrowserFragmentTest { + + private lateinit var store: BrowserStore + private lateinit var testTab: TabSessionState + private lateinit var browserFragment: BrowserFragment + private lateinit var view: View + private lateinit var homeActivity: HomeActivity + private lateinit var fenixApplication: FenixApplication + private lateinit var context: Context + private lateinit var lifecycleOwner: MockedLifecycleOwner + + private val testDispatcher = TestCoroutineDispatcher() + + @get:Rule + val coroutinesTestRule = MainCoroutineRule(testDispatcher) + + @Before + fun setup() { + context = mockk(relaxed = true) + fenixApplication = mockk(relaxed = true) + every { context.application } returns fenixApplication + + homeActivity = mockk(relaxed = true) + view = mockk(relaxed = true) + lifecycleOwner = MockedLifecycleOwner(Lifecycle.State.STARTED) + + browserFragment = spyk(BrowserFragment()) + every { browserFragment.view } returns view + every { browserFragment.isAdded } returns true + every { browserFragment.browserToolbarView } returns mockk(relaxed = true) + every { browserFragment.activity } returns homeActivity + every { browserFragment.lifecycle } returns lifecycleOwner.lifecycle + every { browserFragment.requireContext() } returns context + every { browserFragment.initializeUI(any()) } returns mockk() + every { browserFragment.fullScreenChanged(any()) } returns Unit + every { browserFragment.resumeDownloadDialogState(any(), any(), any(), any(), any()) } returns Unit + + store = BrowserStore() + every { context.components.core.store } returns store + testTab = createTab(url = "https://mozilla.org") + } + + @Test + fun `GIVEN fragment is added WHEN selected tab changes THEN theme is updated`() { + browserFragment.observeTabSelection(store) + verify(exactly = 0) { browserFragment.updateThemeForSession(testTab) } + + addAndSelectTab(testTab) + verify(exactly = 1) { browserFragment.updateThemeForSession(testTab) } + } + + @Test + fun `GIVEN fragment is removing WHEN selected tab changes THEN theme is not updated`() { + every { browserFragment.isRemoving } returns true + browserFragment.observeTabSelection(store) + + addAndSelectTab(testTab) + verify(exactly = 0) { browserFragment.updateThemeForSession(testTab) } + } + + @Test + fun `GIVEN browser UI is not initialized WHEN selected tab changes THEN browser UI is initialized`() { + browserFragment.observeTabSelection(store) + verify(exactly = 0) { browserFragment.initializeUI(view) } + + addAndSelectTab(testTab) + verify(exactly = 1) { browserFragment.initializeUI(view) } + } + + @Test + fun `GIVEN browser UI is initialized WHEN selected tab changes THEN toolbar is expanded`() { + browserFragment.browserInitialized = true + browserFragment.observeTabSelection(store) + + val toolbar: BrowserToolbarView = mockk(relaxed = true) + every { browserFragment.browserToolbarView } returns toolbar + + val newSelectedTab = createTab("https://firefox.com") + addAndSelectTab(newSelectedTab) + verify(exactly = 1) { toolbar.expand() } + } + + @Test + fun `GIVEN browser UI is initialized WHEN selected tab changes THEN full screen mode is exited`() { + browserFragment.browserInitialized = true + browserFragment.observeTabSelection(store) + + val newSelectedTab = createTab("https://firefox.com") + addAndSelectTab(newSelectedTab) + verify(exactly = 1) { browserFragment.fullScreenChanged(false) } + } + + @Test + fun `GIVEN browser UI is initialized WHEN selected tab changes THEN download dialog is resumed`() { + browserFragment.browserInitialized = true + browserFragment.observeTabSelection(store) + + val newSelectedTab = createTab("https://firefox.com") + addAndSelectTab(newSelectedTab) + verify(exactly = 1) { + browserFragment.resumeDownloadDialogState(newSelectedTab.id, store, view, context, any()) + } + } + + @Test + fun `WHEN url changes THEN toolbar is expanded`() { + addAndSelectTab(testTab) + browserFragment.expandToolbarOnNavigation(store) + + val toolbar: BrowserToolbarView = mockk(relaxed = true) + every { browserFragment.browserToolbarView } returns toolbar + + store.dispatch(ContentAction.UpdateUrlAction(testTab.id, "https://firefox.com")).joinBlocking() + verify(exactly = 1) { toolbar.expand() } + } + + @Test + fun `WHEN load request is triggered THEN toolbar is expanded`() { + addAndSelectTab(testTab) + browserFragment.expandToolbarOnNavigation(store) + + val toolbar: BrowserToolbarView = mockk(relaxed = true) + every { browserFragment.browserToolbarView } returns toolbar + + store.dispatch(ContentAction.UpdateLoadRequestAction( + testTab.id, + LoadRequestState("https://firefox.com", false, true)) + ).joinBlocking() + verify(exactly = 1) { toolbar.expand() } + } + + private fun addAndSelectTab(tab: TabSessionState) { + store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking() + store.dispatch(TabListAction.SelectTabAction(tab.id)).joinBlocking() + } + + internal class MockedLifecycleOwner(initialState: Lifecycle.State) : LifecycleOwner { + val lifecycleRegistry = LifecycleRegistry(this).apply { + currentState = initialState + } + + override fun getLifecycle(): Lifecycle = lifecycleRegistry + } +} From c330426bb7dbf2d5c9c8e8a69a4842a93401972a Mon Sep 17 00:00:00 2001 From: Christian Sadilek Date: Tue, 27 Oct 2020 16:43:57 -0400 Subject: [PATCH 021/199] No issue: Fix onConfigurationChange crashes in Debug build --- .../main/java/org/mozilla/fenix/FenixApplication.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 086035b13..64a76a067 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -431,8 +431,17 @@ open class FenixApplication : LocaleAwareApplication(), Provider { // https://issuetracker.google.com/issues/143570309#comment3 applicationContext.resources.configuration.uiMode = config.uiMode - // random StrictMode onDiskRead violation even when Fenix is not running in the background. - components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) { + if (isMainProcess()) { + // We can only do this on the main process as resetAfter will access components.core, which + // will initialize the engine and create an additional GeckoRuntime from the Gecko + // child process, causing a crash. + + // There's a strict mode violation in A-Cs LocaleAwareApplication which + // reads from shared prefs: https://github.com/mozilla-mobile/android-components/issues/8816 + components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) { + super.onConfigurationChanged(config) + } + } else { super.onConfigurationChanged(config) } } From bb7d821804274ba3057952ad55bc9e12ac39dfa2 Mon Sep 17 00:00:00 2001 From: Arturo Mejia Date: Tue, 27 Oct 2020 18:03:20 -0400 Subject: [PATCH 022/199] Fix AddonsManagementFragment detekt error --- .../java/org/mozilla/fenix/addons/AddonsManagementFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt b/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt index 6ed254466..059ae67e0 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt @@ -41,7 +41,7 @@ import java.util.concurrent.CancellationException /** * Fragment use for managing add-ons. */ -@Suppress("TooManyFunctions") +@Suppress("TooManyFunctions", "LargeClass") class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management) { /** From 5739140ff31b9513fa9072e96d77f41e41a35287 Mon Sep 17 00:00:00 2001 From: Mozilla L10n Automation Bot Date: Wed, 28 Oct 2020 00:03:51 +0000 Subject: [PATCH 023/199] Import l10n. --- app/src/main/res/values-ar/strings.xml | 3 +++ app/src/main/res/values-ast/strings.xml | 8 ++++++ app/src/main/res/values-cs/strings.xml | 34 +++++++++++++++++++++++++ app/src/main/res/values-su/strings.xml | 29 +++++++++++++++++++++ 4 files changed, 74 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 8681e0d10..65426da37 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1285,6 +1285,9 @@ عُطّلت الحماية في هذا الموقع عُطّلت الحماية الموسّعة من التعقب لهذه المواقع + + عُد حقوقك diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index 31c3f61ca..bfe511e91 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -340,6 +340,9 @@ Encaboxar + + Modificóse la coleición de complementos. L\'aplicación va zarrase p\'aplicar los cambeos… + Sincronizar agora @@ -726,6 +729,8 @@ Allugamientu Avisos + + Almacenamientu permanente Entrugar pa permitir @@ -1204,6 +1209,9 @@ DESACTIVÓSE nesti sitiu La proteición ameyorada escontra\'l rastrexu ta desactivada pa estos sitios web + + Dir p\'atrás Los tos drechos diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 4e8b6e594..c248ea96f 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -220,6 +220,8 @@ Zjistit více + + Otevřít nový panel Firefoxu Vyhledávání @@ -350,6 +352,20 @@ Oznámení + + + Vlastní sbírka doplňků + + OK + + Zrušit + + Název sbírky + + Vlastník sbírky (ID uživatele) + + Nastavení sbírky změněno. Pro aplikování změn se nyní aplikace ukončí… + Synchronizovat @@ -536,6 +552,15 @@ Nemáte žádné nedávno zavřené panely + + + Panely + + Zobrazení panelů + + Seznam + + Mřížka Zavírat panely @@ -620,6 +645,8 @@ Otevřít panely + + Název sbírky Odebrat @@ -658,6 +685,10 @@ Položek ke smazání: %1$d + + Dnes + + Včera Posledních 24 hodin @@ -1606,6 +1637,9 @@ The first parameter is the name of the app (e.g. Firefox Preview) --> Využijte aplikaci %s naplno. + + Pro více informací klepněte zde + Uložte si důležité věci do sbírek diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml index fd1bdd959..a6a0606b6 100644 --- a/app/src/main/res/values-su/strings.xml +++ b/app/src/main/res/values-su/strings.xml @@ -292,6 +292,8 @@ Téma Tepas + + Réngkak Sesuaikeun @@ -328,6 +330,8 @@ Paluruh markah + + Paluruh tab anu singkron Setélan akun @@ -343,11 +347,21 @@ Iber + + + Koléksi Émboh sakahayang + + Heug + + Bolay Ngaran koléksi Pamilik koléksi (Sandiasma) + + Koléksi Émboh geus dirobah. Kaluar ti aplikasi pikeun nerapkeun parobahan… + Singkronkeun ayeuna @@ -534,6 +548,15 @@ Taya tab nu anyar ditutup di dieu + + + Tab + + Panémbong tab + + Béréndélan + + Grid Tutup tab @@ -610,6 +633,8 @@ Ganti ngaran koléksi Buka tab + + Ngaran koléksi Piceun @@ -648,6 +673,10 @@ is a digit showing the number of items you have selected --> Hapus %1$d item + + Poé ieu + + Kamari 24 jam terakhir From e36e61b2c325a16f5d908bf24daa5132c7ed0679 Mon Sep 17 00:00:00 2001 From: Oana Horvath Date: Wed, 28 Oct 2020 12:51:16 +0200 Subject: [PATCH 024/199] For #10690: re-enable editBookmarkTest --- .../java/org/mozilla/fenix/ui/BookmarksTest.kt | 6 ------ .../java/org/mozilla/fenix/ui/robots/BookmarksRobot.kt | 8 ++------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt index 59e08ac3b..b011e6181 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt @@ -12,7 +12,6 @@ import mozilla.appservices.places.BookmarkRoot import okhttp3.mockwebserver.MockWebServer import org.junit.After import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.mozilla.fenix.R @@ -151,7 +150,6 @@ class BookmarksTest { } } - @Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/10690") @Test fun editBookmarkTest() { val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -164,7 +162,6 @@ class BookmarksTest { RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1) IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!) }.openThreeDotMenu(defaultWebPage.url) { - IdlingRegistry.getInstance().unregister(bookmarksListIdlingResource!!) }.clickEdit { verifyEditBookmarksView() verifyBookmarkNameEditBox() @@ -173,9 +170,6 @@ class BookmarksTest { changeBookmarkTitle(testBookmark.title) changeBookmarkUrl(testBookmark.url) saveEditBookmark() - - IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!) - verifyBookmarkTitle(testBookmark.title) verifyBookmarkedURL(testBookmark.url) verifyKeyboardHidden() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BookmarksRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BookmarksRobot.kt index bd5a17f06..f7be13f15 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BookmarksRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BookmarksRobot.kt @@ -27,7 +27,6 @@ import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By.res -import androidx.test.uiautomator.By.text import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiSelector import org.hamcrest.Matchers.allOf @@ -58,10 +57,7 @@ class BookmarksRobot { } fun verifyBookmarkTitle(title: String) { - mDevice.waitNotNull( - Until.findObject(text(title)), - TestAssetHelper.waitingTime - ) + mDevice.findObject(UiSelector().text(title)).waitForExists(waitingTime) assertBookmarkTitle(title) } @@ -164,7 +160,7 @@ class BookmarksRobot { fun saveEditBookmark() { saveBookmarkButton().click() - mDevice.waitNotNull(Until.findObject(text("Bookmarks"))) + mDevice.findObject(UiSelector().resourceId("R.id.bookmark_list")).waitForExists(waitingTime) } fun clickParentFolderSelector() = bookmarkFolderSelector().click() From 9748c65c7163c21629e3929973966e64cd297655 Mon Sep 17 00:00:00 2001 From: mcarare Date: Wed, 28 Oct 2020 12:43:47 +0200 Subject: [PATCH 025/199] For #15413: Use proper url when bookmarking a page. --- .../fenix/browser/BaseBrowserFragment.kt | 14 ++++--- .../toolbar/BrowserToolbarMenuController.kt | 4 +- ...DefaultBrowserToolbarMenuControllerTest.kt | 40 +++++++++++++++++-- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt index a5f7b5fcf..7164ed959 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -321,7 +321,11 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, browserAnimator = browserAnimator, customTabSession = customTabSessionId?.let { sessionManager.findSessionById(it) }, openInFenixIntent = openInFenixIntent, - bookmarkTapped = { viewLifecycleOwner.lifecycleScope.launch { bookmarkTapped(it) } }, + bookmarkTapped = { url: String, title: String -> + viewLifecycleOwner.lifecycleScope.launch { + bookmarkTapped(url, title) + } + }, scope = viewLifecycleOwner.lifecycleScope, tabCollectionStorage = requireComponents.core.tabCollectionStorage, topSitesStorage = requireComponents.core.topSitesStorage, @@ -1052,10 +1056,10 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, } } - private suspend fun bookmarkTapped(session: Session) = withContext(IO) { + private suspend fun bookmarkTapped(sessionUrl: String, sessionTitle: String) = withContext(IO) { val bookmarksStorage = requireComponents.core.bookmarksStorage val existing = - bookmarksStorage.getBookmarksWithUrl(session.url).firstOrNull { it.url == session.url } + bookmarksStorage.getBookmarksWithUrl(sessionUrl).firstOrNull { it.url == sessionUrl } if (existing != null) { // Bookmark exists, go to edit fragment withContext(Main) { @@ -1068,8 +1072,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, // Save bookmark, then go to edit fragment val guid = bookmarksStorage.addItem( BookmarkRoot.Mobile.id, - url = session.url, - title = session.title, + url = sessionUrl, + title = sessionTitle, position = null ) diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt index 9c0734c37..90f91763f 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt @@ -65,7 +65,7 @@ class DefaultBrowserToolbarMenuController( private val swipeRefresh: SwipeRefreshLayout, private val customTabSession: Session?, private val openInFenixIntent: Intent, - private val bookmarkTapped: (Session) -> Unit, + private val bookmarkTapped: (String, String) -> Unit, private val scope: CoroutineScope, private val tabCollectionStorage: TabCollectionStorage, private val topSitesStorage: DefaultTopSitesStorage, @@ -273,7 +273,7 @@ class DefaultBrowserToolbarMenuController( } ToolbarMenu.Item.Bookmark -> { sessionManager.selectedSession?.let { - bookmarkTapped(it) + getProperUrl(it)?.let { url -> bookmarkTapped(url, it.title) } } } ToolbarMenu.Item.Bookmarks -> browserAnimator.captureEngineViewAndDrawStatically { diff --git a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt index 4ed326317..a5c2d6c31 100644 --- a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt @@ -73,7 +73,7 @@ class DefaultBrowserToolbarMenuControllerTest { @RelaxedMockK private lateinit var activity: HomeActivity @RelaxedMockK private lateinit var navController: NavController @RelaxedMockK private lateinit var findInPageLauncher: () -> Unit - @RelaxedMockK private lateinit var bookmarkTapped: (Session) -> Unit + @RelaxedMockK private lateinit var bookmarkTapped: (String, String) -> Unit @RelaxedMockK private lateinit var sessionManager: SessionManager @RelaxedMockK private lateinit var currentSession: Session @RelaxedMockK private lateinit var openInFenixIntent: Intent @@ -224,14 +224,48 @@ class DefaultBrowserToolbarMenuControllerTest { } @Test - fun handleToolbarBookmarkPress() = runBlockingTest { + fun handleToolbarBookmarkPressWithReaderModeInactive() = runBlockingTest { val item = ToolbarMenu.Item.Bookmark + val title = "Mozilla" + val readerUrl = "moz-extension://1234" + val readerTab = createTab( + url = readerUrl, + readerState = ReaderState(active = false, activeUrl = "https://1234.org"), + title = title + ) + browserStore = + BrowserStore(BrowserState(tabs = listOf(readerTab), selectedTabId = readerTab.id)) + every { currentSession.id } returns readerTab.id + every { currentSession.title } returns title + every { currentSession.url } returns "https://mozilla.org" + + val controller = createController(scope = this) + controller.handleToolbarItemInteraction(item) + + verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.BOOKMARK)) } + verify { bookmarkTapped("https://mozilla.org", title) } + } + + @Test + fun handleToolbarBookmarkPressWithReaderModeActive() = runBlockingTest { + val item = ToolbarMenu.Item.Bookmark + val title = "Mozilla" + val readerUrl = "moz-extension://1234" + val readerTab = createTab( + url = readerUrl, + readerState = ReaderState(active = true, activeUrl = "https://mozilla.org"), + title = title + ) + browserStore = BrowserStore(BrowserState(tabs = listOf(readerTab), selectedTabId = readerTab.id)) + every { currentSession.id } returns readerTab.id + every { currentSession.title } returns title + every { currentSession.url } returns readerUrl val controller = createController(scope = this) controller.handleToolbarItemInteraction(item) verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.BOOKMARK)) } - verify { bookmarkTapped(currentSession) } + verify { bookmarkTapped("https://mozilla.org", title) } } @Test From dec63564a3e2857d1b90144e76755a129fb7c9e4 Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Wed, 28 Oct 2020 15:34:54 +0000 Subject: [PATCH 026/199] Update Android Components version to 64.0.20201027143116. --- buildSrc/src/main/java/AndroidComponents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 97ab58835..057cfa995 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 = "64.0.20201027134712" + const val VERSION = "64.0.20201027143116" } From 0ee7e1c28de2a51379c672a0fc084e6bd2ff7105 Mon Sep 17 00:00:00 2001 From: Mihai Adrian Carare <48995920+mcarare@users.noreply.github.com> Date: Wed, 28 Oct 2020 22:46:02 +0200 Subject: [PATCH 027/199] For # 15929: Remove the search widget discoverability experiment. (#16081) * For #15929: Remove SearchWidgetCFR telemetry. * For #15929: Remove SearchWidgetCFR and search widget experiment. * For #15929: Remove unit tests references to search widget experiment. --- app/metrics.yaml | 59 ---------- .../org/mozilla/fenix/ExperimentsManager.kt | 39 ------- .../org/mozilla/fenix/FenixApplication.kt | 7 +- .../org/mozilla/fenix/cfr/SearchWidgetCFR.kt | 107 ------------------ .../mozilla/fenix/components/metrics/Event.kt | 4 - .../components/metrics/GleanMetricsService.kt | 13 --- .../org/mozilla/fenix/home/HomeFragment.kt | 17 --- .../SessionControlController.kt | 1 - .../fenix/search/SearchDialogController.kt | 3 - .../java/org/mozilla/fenix/utils/Settings.kt | 51 --------- .../search_widget_illustration.png | Bin 23276 -> 0 bytes app/src/main/res/layout/search_widget_cfr.xml | 102 ----------------- app/src/main/res/values/preference_keys.xml | 5 - app/src/main/res/values/strings.xml | 8 -- .../DefaultSessionControlControllerTest.kt | 1 - .../search/SearchDialogControllerTest.kt | 1 - docs/metrics.md | 4 - 17 files changed, 2 insertions(+), 420 deletions(-) delete mode 100644 app/src/main/java/org/mozilla/fenix/ExperimentsManager.kt delete mode 100644 app/src/main/java/org/mozilla/fenix/cfr/SearchWidgetCFR.kt delete mode 100644 app/src/main/res/drawable-xhdpi/search_widget_illustration.png delete mode 100644 app/src/main/res/layout/search_widget_cfr.xml diff --git a/app/metrics.yaml b/app/metrics.yaml index fd6de67c9..1a48b2ad2 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -2591,65 +2591,6 @@ search_widget: - fenix-core@mozilla.com expires: "2021-08-01" -search_widget_cfr: - displayed: - type: event - description: | - The search widget cfr was displayed. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/9488 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/10958 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - data_sensitivity: - - interaction - notification_emails: - - fenix-core@mozilla.com - expires: "2021-08-01" - add_widget_pressed: - type: event - description: | - The user pressed the "add widget" button. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/9488 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/10958 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - data_sensitivity: - - interaction - notification_emails: - - fenix-core@mozilla.com - expires: "2021-08-01" - not_now_pressed: - type: event - description: | - The user pressed the "not now" button. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/9488 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/10958 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - data_sensitivity: - - interaction - notification_emails: - - fenix-core@mozilla.com - expires: "2021-08-01" - canceled: - type: event - description: | - The user dismissed the search widget cfr by - tapping outside of the prompt - bugs: - - https://github.com/mozilla-mobile/fenix/issues/9488 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/10958 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - data_sensitivity: - - interaction - notification_emails: - - fenix-core@mozilla.com - expires: "2021-08-01" - private_browsing_mode: garbage_icon: type: event diff --git a/app/src/main/java/org/mozilla/fenix/ExperimentsManager.kt b/app/src/main/java/org/mozilla/fenix/ExperimentsManager.kt deleted file mode 100644 index d93b77bad..000000000 --- a/app/src/main/java/org/mozilla/fenix/ExperimentsManager.kt +++ /dev/null @@ -1,39 +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 - -import android.content.Context -import mozilla.components.service.experiments.Experiments -import org.mozilla.fenix.ext.settings - -object ExperimentsManager { - - fun optOutSearchWidgetExperiment(context: Context) { - // Release user has opted out of search widget CFR experiment, reset them to not see it. - context.settings().setSearchWidgetExperiment(false) - } - - fun initSearchWidgetExperiment(context: Context) { - // When the `search-widget-discoverability` experiment is active,set the pref to either - // show or hide the search widget CFR (given other criteria are met as well). - // Note that this will not take effect the first time the application has launched, - // since there won't be enough time for the experiments library to get a list of experiments. - // It will take effect the second time the application is launched. - Experiments.withExperiment("fenix-search-widget") { branchName -> - when (branchName) { - "control_no_cfr" -> { - context.settings().setSearchWidgetExperiment(false) - } - "treatment_cfr" -> { - context.settings().setSearchWidgetExperiment(true) - } - else -> { - // No branch matches so we're defaulting to no CFR - context.settings().setSearchWidgetExperiment(false) - } - } - } - } -} diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 64a76a067..d147eb010 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -171,24 +171,21 @@ open class FenixApplication : LocaleAwareApplication(), Provider { } fun queueInitExperiments() { + @Suppress("ControlFlowWithEmptyBody") if (settings().isExperimentationEnabled) { queue.runIfReadyOrQueue { Experiments.initialize( applicationContext = applicationContext, - onExperimentsUpdated = { - ExperimentsManager.initSearchWidgetExperiment(this) - }, + onExperimentsUpdated = null, configuration = mozilla.components.service.experiments.Configuration( httpClient = components.core.client, kintoEndpoint = KINTO_ENDPOINT_PROD ) ) - ExperimentsManager.initSearchWidgetExperiment(this) } } else { // We should make a better way to opt out for when we have more experiments // See https://github.com/mozilla-mobile/fenix/issues/6278 - ExperimentsManager.optOutSearchWidgetExperiment(this) } } diff --git a/app/src/main/java/org/mozilla/fenix/cfr/SearchWidgetCFR.kt b/app/src/main/java/org/mozilla/fenix/cfr/SearchWidgetCFR.kt deleted file mode 100644 index 2b37185b0..000000000 --- a/app/src/main/java/org/mozilla/fenix/cfr/SearchWidgetCFR.kt +++ /dev/null @@ -1,107 +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.cfr - -import android.app.Dialog -import android.content.Context -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.view.Gravity -import android.view.LayoutInflater -import android.view.View -import androidx.core.view.isVisible -import androidx.core.view.marginTop -import kotlinx.android.synthetic.main.search_widget_cfr.view.* -import kotlinx.android.synthetic.main.tracking_protection_onboarding_popup.view.drop_down_triangle -import kotlinx.android.synthetic.main.tracking_protection_onboarding_popup.view.pop_up_triangle -import org.mozilla.fenix.R -import org.mozilla.fenix.components.SearchWidgetCreator -import org.mozilla.fenix.components.metrics.Event -import org.mozilla.fenix.components.metrics.MetricController -import org.mozilla.fenix.components.toolbar.ToolbarPosition -import org.mozilla.fenix.utils.Settings - -/** - * Displays a CFR above the HomeFragment toolbar that recommends usage / installation of the search widget. - */ -class SearchWidgetCFR( - private val context: Context, - private val settings: Settings, - private val metrics: MetricController, - private val getToolbar: () -> View -) { - - fun displayIfNecessary() { - if (settings.isInSearchWidgetExperiment && - settings.shouldDisplaySearchWidgetCfr() && - !isShown - ) { - isShown = true - showSearchWidgetCFR() - } - } - - @Suppress("InflateParams") - private fun showSearchWidgetCFR() { - settings.lastCfrShownTimeInMillis = System.currentTimeMillis() - settings.incrementSearchWidgetCFRDisplayed() - - val searchWidgetCFRDialog = Dialog(context) - val layout = LayoutInflater.from(context) - .inflate(R.layout.search_widget_cfr, null) - val toolbarPosition = settings.toolbarPosition - - layout.drop_down_triangle.isVisible = toolbarPosition == ToolbarPosition.TOP - layout.pop_up_triangle.isVisible = toolbarPosition == ToolbarPosition.BOTTOM - - val toolbar = getToolbar() - - val gravity = Gravity.CENTER_HORIZONTAL or toolbarPosition.androidGravity - - layout.cfr_neg_button.setOnClickListener { - metrics.track(Event.SearchWidgetCFRNotNowPressed) - searchWidgetCFRDialog.dismiss() - settings.manuallyDismissSearchWidgetCFR() - } - - layout.cfr_pos_button.setOnClickListener { - metrics.track(Event.SearchWidgetCFRAddWidgetPressed) - SearchWidgetCreator.createSearchWidget(context) - searchWidgetCFRDialog.dismiss() - settings.manuallyDismissSearchWidgetCFR() - } - - searchWidgetCFRDialog.apply { - setContentView(layout) - } - - searchWidgetCFRDialog.window?.let { - it.setGravity(gravity) - val attr = it.attributes - attr.y = - (toolbar.y + toolbar.height - toolbar.marginTop - toolbar.paddingTop).toInt() - it.attributes = attr - it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - } - - searchWidgetCFRDialog.setOnCancelListener { - isShown = false - metrics.track(Event.SearchWidgetCFRCanceled) - } - - searchWidgetCFRDialog.setOnDismissListener { - isShown = false - settings.incrementSearchWidgetCFRDismissed() - } - - searchWidgetCFRDialog.show() - metrics.track(Event.SearchWidgetCFRDisplayed) - } - - companion object { - // Used to ensure multiple dialogs are not shown on top of each other - var isShown = false - } -} diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt index bd79b6f9f..39cc5b48c 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt @@ -152,10 +152,6 @@ sealed class Event { object FennecToFenixMigrated : Event() object AddonsOpenInSettings : Event() object VoiceSearchTapped : Event() - object SearchWidgetCFRDisplayed : Event() - object SearchWidgetCFRCanceled : Event() - object SearchWidgetCFRNotNowPressed : Event() - object SearchWidgetCFRAddWidgetPressed : Event() object SearchWidgetInstalled : Event() object OnboardingAutoSignIn : Event() object OnboardingManualSignIn : Event() diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt index 86ddc6a51..89101ff0b 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt @@ -44,7 +44,6 @@ import org.mozilla.fenix.GleanMetrics.SearchDefaultEngine import org.mozilla.fenix.GleanMetrics.SearchShortcuts import org.mozilla.fenix.GleanMetrics.SearchSuggestions import org.mozilla.fenix.GleanMetrics.SearchWidget -import org.mozilla.fenix.GleanMetrics.SearchWidgetCfr import org.mozilla.fenix.GleanMetrics.SyncAccount import org.mozilla.fenix.GleanMetrics.SyncAuth import org.mozilla.fenix.GleanMetrics.Tab @@ -579,18 +578,6 @@ private val Event.wrapper: EventWrapper<*>? is Event.VoiceSearchTapped -> EventWrapper( { VoiceSearch.tapped.record(it) } ) - is Event.SearchWidgetCFRDisplayed -> EventWrapper( - { SearchWidgetCfr.displayed.record(it) } - ) - is Event.SearchWidgetCFRCanceled -> EventWrapper( - { SearchWidgetCfr.canceled.record(it) } - ) - is Event.SearchWidgetCFRNotNowPressed -> EventWrapper( - { SearchWidgetCfr.notNowPressed.record(it) } - ) - is Event.SearchWidgetCFRAddWidgetPressed -> EventWrapper( - { SearchWidgetCfr.addWidgetPressed.record(it) } - ) is Event.TabCounterMenuItemTapped -> EventWrapper( { Events.tabCounterMenuAction.record(it) }, { Events.tabCounterMenuActionKeys.valueOf(it) } 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 7a6e20d2e..7f22d1c79 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -28,7 +28,6 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.content.ContextCompat -import androidx.core.view.doOnLayout import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.fragment.app.Fragment @@ -75,7 +74,6 @@ import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.browser.BrowserAnimator.Companion.getToolbarNavOptions import org.mozilla.fenix.browser.browsingmode.BrowsingMode -import org.mozilla.fenix.cfr.SearchWidgetCFR import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.PrivateShortcutCreateManager import org.mozilla.fenix.components.StoreProvider @@ -427,21 +425,6 @@ class HomeFragment : Fragment() { } } - // We call this onLayout so that the bottom bar width is correctly set for us to center - // the CFR in. - view.toolbar_wrapper.doOnLayout { - val willNavigateToSearch = !bundleArgs.getBoolean(FOCUS_ON_ADDRESS_BAR) - if (!browsingModeManager.mode.isPrivate && !willNavigateToSearch) { - SearchWidgetCFR( - context = view.context, - settings = view.context.settings(), - metrics = view.context.components.analytics.metrics - ) { - view.toolbar_wrapper - }.displayIfNecessary() - } - } - if (browsingModeManager.mode.isPrivate) { requireActivity().window.addFlags(FLAG_SECURE) } else { diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt index c2a35df94..8b0ff8940 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt @@ -414,7 +414,6 @@ class DefaultSessionControlController( Event.EnteredUrl(false) } else { val searchAccessPoint = Event.PerformedSearch.SearchAccessPoint.ACTION - activity.settings().incrementActiveSearchCount() searchAccessPoint.let { sap -> MetricsUtils.createSearchEvent( activity.components.search.provider.getDefaultEngine(activity), diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchDialogController.kt b/app/src/main/java/org/mozilla/fenix/search/SearchDialogController.kt index 742f7fab1..c6ffbf66b 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchDialogController.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchDialogController.kt @@ -91,8 +91,6 @@ class SearchDialogController( val event = if (url.isUrl()) { Event.EnteredUrl(false) } else { - settings.incrementActiveSearchCount() - val searchAccessPoint = when (store.state.searchAccessPoint) { Event.PerformedSearch.SearchAccessPoint.NONE -> Event.PerformedSearch.SearchAccessPoint.ACTION else -> store.state.searchAccessPoint @@ -149,7 +147,6 @@ class SearchDialogController( } override fun handleSearchTermsTapped(searchTerms: String) { - settings.incrementActiveSearchCount() clearToolbarFocus() activity.openToBrowserAndLoad( diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index d9fd08908..b17e5ba22 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -195,57 +195,6 @@ class Settings(private val appContext: Context) : PreferencesHolder { true ) - private val activeSearchCount = counterPreference( - appContext.getPreferenceKey(R.string.pref_key_search_count) - ) - - fun incrementActiveSearchCount() = activeSearchCount.increment() - - private val isActiveSearcher: Boolean - get() = activeSearchCount.value > 2 - - fun shouldDisplaySearchWidgetCfr(): Boolean = canShowCfr && isActiveSearcher && - searchWidgetCFRDismissCount.underMaxCount() && - !searchWidgetInstalled && - !searchWidgetCFRManuallyDismissed - - private val searchWidgetCFRDisplayCount = counterPreference( - appContext.getPreferenceKey(R.string.pref_key_search_widget_cfr_display_count) - ) - - fun incrementSearchWidgetCFRDisplayed() = searchWidgetCFRDisplayCount.increment() - - private val searchWidgetCFRManuallyDismissed by booleanPreference( - appContext.getPreferenceKey(R.string.pref_key_search_widget_cfr_manually_dismissed), - default = false - ) - - fun manuallyDismissSearchWidgetCFR() { - preferences.edit().putBoolean( - appContext.getPreferenceKey(R.string.pref_key_search_widget_cfr_manually_dismissed), - true - ).apply() - } - - private val searchWidgetCFRDismissCount = counterPreference( - appContext.getPreferenceKey(R.string.pref_key_search_widget_cfr_dismiss_count), - maxCount = 3 - ) - - fun incrementSearchWidgetCFRDismissed() = searchWidgetCFRDismissCount.increment() - - val isInSearchWidgetExperiment by booleanPreference( - appContext.getPreferenceKey(R.string.pref_key_is_in_search_widget_experiment), - default = false - ) - - fun setSearchWidgetExperiment(value: Boolean) { - preferences.edit().putBoolean( - appContext.getPreferenceKey(R.string.pref_key_is_in_search_widget_experiment), - value - ).apply() - } - var defaultSearchEngineName by stringPreference( appContext.getPreferenceKey(R.string.pref_key_search_engine), default = "" diff --git a/app/src/main/res/drawable-xhdpi/search_widget_illustration.png b/app/src/main/res/drawable-xhdpi/search_widget_illustration.png deleted file mode 100644 index dc6539813c9afdba638ef3b44e01af4c0042f227..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23276 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 z - - - - - - - - - - - - -