From 09d0688e6483d0e2ca33009c7a77855f29b9556d Mon Sep 17 00:00:00 2001 From: liuche Date: Mon, 20 Jul 2020 15:09:58 -0700 Subject: [PATCH 01/31] CI for PR #9705 (#12748) * #3880. Update detekt to 1.9.1 * #3880. Use `AbsentOrWrongFileLicense` detekt rule * #3880. Update detekt baseline Co-authored-by: Denys M --- build.gradle | 8 ++--- buildSrc/src/main/java/Dependencies.kt | 2 +- config/detekt-baseline.xml | 50 ++++++++++++++++++++++++++ config/detekt.yml | 6 ++-- config/license.template | 3 ++ 5 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 config/detekt-baseline.xml create mode 100644 config/license.template diff --git a/build.gradle b/build.gradle index 6ca271f71..7b9f7dc3c 100644 --- a/build.gradle +++ b/build.gradle @@ -72,7 +72,7 @@ buildscript { } plugins { - id("io.gitlab.arturbosch.detekt").version("1.6.0") + id("io.gitlab.arturbosch.detekt").version("1.9.1") } allprojects { @@ -133,8 +133,7 @@ allprojects { kotlinOptions.jvmTarget = "1.8" kotlinOptions.allWarningsAsErrors = true kotlinOptions.freeCompilerArgs += [ - "-Xuse-experimental=kotlin.Experimental", - "-Xskip-runtime-version-check" + "-Xuse-experimental=kotlin.Experimental" ] } } @@ -145,9 +144,10 @@ task clean(type: Delete) { detekt { // The version number is duplicated, please refer to plugins block for more details - version = "1.6.0" + version = "1.9.1" input = files("$projectDir/app/src") config = files("$projectDir/config/detekt.yml") + baseline = file("$projectDir/config/detekt-baseline.xml") reports { html { diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index 3c2ba184e..4421c1b0a 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -11,7 +11,7 @@ object Versions { const val leanplum = "5.4.0" const val osslicenses_plugin = "0.9.5" const val osslicenses_library = "17.0.0" - const val detekt = "1.6.0" + const val detekt = "1.9.1" const val androidx_appcompat = "1.2.0-rc01" const val androidx_biometric = "1.1.0-alpha01" diff --git a/config/detekt-baseline.xml b/config/detekt-baseline.xml new file mode 100644 index 000000000..0da17efa1 --- /dev/null +++ b/config/detekt-baseline.xml @@ -0,0 +1,50 @@ + + + + + AbsentOrWrongFileLicense:AboutItemViewHolder.kt$org.mozilla.fenix.settings.about.viewholders.AboutItemViewHolder.kt + AbsentOrWrongFileLicense:AccountSettingsFragmentStore.kt$org.mozilla.fenix.settings.account.AccountSettingsFragmentStore.kt + AbsentOrWrongFileLicense:AccountSettingsInteractor.kt$org.mozilla.fenix.settings.account.AccountSettingsInteractor.kt + AbsentOrWrongFileLicense:ButtonTipViewHolder.kt$org.mozilla.fenix.home.tips.ButtonTipViewHolder.kt + AbsentOrWrongFileLicense:CustomTabContextMenuCandidate.kt$org.mozilla.fenix.browser.CustomTabContextMenuCandidate.kt + AbsentOrWrongFileLicense:ExceptionsFragmentStore.kt$org.mozilla.fenix.loginexceptions.ExceptionsFragmentStore.kt + AbsentOrWrongFileLicense:ExceptionsFragmentStore.kt$org.mozilla.fenix.trackingprotectionexceptions.ExceptionsFragmentStore.kt + AbsentOrWrongFileLicense:ExceptionsInteractor.kt$org.mozilla.fenix.trackingprotectionexceptions.ExceptionsInteractor.kt + AbsentOrWrongFileLicense:FeatureFlags.kt$org.mozilla.fenix.FeatureFlags.kt + AbsentOrWrongFileLicense:HistoryController.kt$org.mozilla.fenix.library.history.HistoryController.kt + AbsentOrWrongFileLicense:HistoryFragmentStore.kt$org.mozilla.fenix.library.history.HistoryFragmentStore.kt + AbsentOrWrongFileLicense:HistoryInteractor.kt$org.mozilla.fenix.library.history.HistoryInteractor.kt + AbsentOrWrongFileLicense:InflationAwareFeature.kt$org.mozilla.fenix.components.InflationAwareFeature.kt + AbsentOrWrongFileLicense:LoginExceptionsInteractor.kt$org.mozilla.fenix.loginexceptions.LoginExceptionsInteractor.kt + AbsentOrWrongFileLicense:MigrationTelemetryListener.kt$org.mozilla.fenix.migration.MigrationTelemetryListener.kt + AbsentOrWrongFileLicense:Push.kt$org.mozilla.fenix.components.Push.kt + AbsentOrWrongFileLicense:SearchController.kt$org.mozilla.fenix.search.SearchController.kt + AbsentOrWrongFileLicense:SearchFragmentStore.kt$org.mozilla.fenix.search.SearchFragmentStore.kt + AbsentOrWrongFileLicense:SharedPreferenceUpdater.kt$org.mozilla.fenix.settings.SharedPreferenceUpdater.kt + AbsentOrWrongFileLicense:StoreProvider.kt$org.mozilla.fenix.components.StoreProvider.kt + AbsentOrWrongFileLicense:TabCounterMenuItem.kt$org.mozilla.fenix.components.toolbar.TabCounterMenuItem.kt + AbsentOrWrongFileLicense:ToolbarView.kt$org.mozilla.fenix.search.toolbar.ToolbarView.kt + AbsentOrWrongFileLicense:TrackingProtectionStore.kt$org.mozilla.fenix.trackingprotection.TrackingProtectionStore.kt + AbsentOrWrongFileLicense:ViewHolder.kt$org.mozilla.fenix.utils.view.ViewHolder.kt + AbsentOrWrongFileLicense:WhatsNew.kt$org.mozilla.fenix.whatsnew.WhatsNew.kt + AbsentOrWrongFileLicense:WhatsNewStorage.kt$org.mozilla.fenix.whatsnew.WhatsNewStorage.kt + AbsentOrWrongFileLicense:WhatsNewVersion.kt$org.mozilla.fenix.whatsnew.WhatsNewVersion.kt + LongParameterList:BackgroundServices.kt$BackgroundServices$( private val context: Context, private val push: Push, crashReporter: CrashReporter, historyStorage: Lazy<PlacesHistoryStorage>, bookmarkStorage: Lazy<PlacesBookmarksStorage>, passwordsStorage: Lazy<SyncableLoginsStorage>, remoteTabsStorage: Lazy<RemoteTabsStorage> ) + LongParameterList:BookmarkController.kt$DefaultBookmarkController$( private val activity: HomeActivity, private val navController: NavController, private val clipboardManager: ClipboardManager?, private val scope: CoroutineScope, private val store: BookmarkFragmentStore, private val sharedViewModel: BookmarksSharedViewModel, private val loadBookmarkNode: suspend (String) -> BookmarkNode?, private val showSnackbar: (String) -> Unit, private val deleteBookmarkNodes: (Set<BookmarkNode>, Event) -> Unit, private val deleteBookmarkFolder: (BookmarkNode) -> Unit, private val invokePendingDeletion: () -> Unit ) + LongParameterList:BrowserToolbarController.kt$DefaultBrowserToolbarController$( private val activity: HomeActivity, private val navController: NavController, private val readerModeController: ReaderModeController, private val sessionFeature: ViewBoundFeatureWrapper<SessionFeature>, private val sessionManager: SessionManager, private val findInPageLauncher: () -> Unit, private val engineView: EngineView, private val browserAnimator: BrowserAnimator, private val swipeRefresh: SwipeRefreshLayout, private val customTabSession: Session?, private val openInFenixIntent: Intent, private val bookmarkTapped: (Session) -> Unit, private val scope: CoroutineScope, private val tabCollectionStorage: TabCollectionStorage, private val topSiteStorage: TopSiteStorage, private val onTabCounterClicked: () -> Unit, private val onCloseTab: (Session) -> Unit ) + LongParameterList:CollectionCreationController.kt$DefaultCollectionCreationController$( private val store: CollectionCreationStore, private val dismiss: () -> Unit, private val metrics: MetricController, private val tabCollectionStorage: TabCollectionStorage, private val sessionManager: SessionManager, private val ioScope: CoroutineScope ) + LongParameterList:CollectionCreationTabListAdapter.kt$TabDiffUtil$( val old: List<Tab>, val new: List<Tab>, val oldSelected: Set<Tab>, val newSelected: Set<Tab>, val oldHideCheckboxes: Boolean, val newHideCheckboxes: Boolean ) + LongParameterList:CustomTabsIntegration.kt$CustomTabsIntegration$( sessionManager: SessionManager, toolbar: BrowserToolbar, sessionId: String, activity: Activity, onItemTapped: (ToolbarMenu.Item) -> Unit = {}, shouldReverseItems: Boolean, isPrivate: Boolean ) + LongParameterList:DefaultToolbarMenu.kt$DefaultToolbarMenu$( private val context: Context, private val sessionManager: SessionManager, private val store: BrowserStore, hasAccountProblem: Boolean = false, shouldReverseItems: Boolean, private val onItemTapped: (ToolbarMenu.Item) -> Unit = {}, private val lifecycleOwner: LifecycleOwner, private val bookmarksStorage: BookmarksStorage ) + LongParameterList:DynamicDownloadDialog.kt$DynamicDownloadDialog$( private val container: ViewGroup, private val downloadState: DownloadState?, private val didFail: Boolean, private val tryAgain: (Long) -> Unit, private val onCannotOpenFile: () -> Unit, private val view: View, private val toolbarHeight: Int, private val onDismiss: () -> Unit ) + LongParameterList:HistoryController.kt$DefaultHistoryController$( private val store: HistoryFragmentStore, private val navController: NavController, private val resources: Resources, private val snackbar: FenixSnackbar, private val clipboardManager: ClipboardManager, private val scope: CoroutineScope, private val openToBrowser: (item: HistoryItem, mode: BrowsingMode?) -> Unit, private val displayDeleteAll: () -> Unit, private val invalidateOptionsMenu: () -> Unit, private val deleteHistoryItems: (Set<HistoryItem>) -> Unit, private val syncHistory: suspend () -> Unit ) + LongParameterList:IntentProcessors.kt$IntentProcessors$( private val context: Context, private val sessionManager: SessionManager, private val sessionUseCases: SessionUseCases, private val searchUseCases: SearchUseCases, private val relationChecker: RelationChecker, private val customTabsStore: CustomTabsServiceStore, private val migrationStore: MigrationStore, private val manifestStorage: ManifestStorage ) + LongParameterList:QuickSettingsController.kt$DefaultQuickSettingsController$( private val context: Context, private val quickSettingsStore: QuickSettingsFragmentStore, private val ioScope: CoroutineScope, private val navController: NavController, private val session: Session?, private var sitePermissions: SitePermissions?, private val settings: Settings, private val permissionStorage: PermissionStorage, private val reload: ReloadUrlUseCase, private val addNewTab: AddNewTabUseCase, private val requestRuntimePermissions: OnNeedToRequestPermissions = { }, private val displayPermissions: () -> Unit, private val dismiss: () -> Unit ) + LongParameterList:SessionControlController.kt$DefaultSessionControlController$( private val activity: HomeActivity, private val fragmentStore: HomeFragmentStore, private val navController: NavController, private val viewLifecycleScope: CoroutineScope, private val getListOfTabs: () -> List<Tab>, private val hideOnboarding: () -> Unit, private val registerCollectionStorageObserver: () -> Unit, private val showDeleteCollectionPrompt: (tabCollection: TabCollection, title: String?, message: String) -> Unit, private val openSettingsScreen: () -> Unit, private val openWhatsNewLink: () -> Unit, private val openPrivacyNotice: () -> Unit, private val showTabTray: () -> Unit ) + LongParameterList:ShareController.kt$DefaultShareController$( private val context: Context, private val shareData: List<ShareData>, private val sendTabUseCases: SendTabUseCases, private val snackbar: FenixSnackbar, private val navController: NavController, private val recentAppsStorage: RecentAppsStorage, private val viewLifecycleScope: CoroutineScope, private val dismiss: (ShareController.Result) -> Unit ) + LongParameterList:ToolbarIntegration.kt$DefaultToolbarIntegration$( context: Context, toolbar: BrowserToolbar, toolbarMenu: ToolbarMenu, domainAutocompleteProvider: DomainAutocompleteProvider, historyStorage: HistoryStorage, sessionManager: SessionManager, sessionId: String? = null, isPrivate: Boolean, interactor: BrowserToolbarViewInteractor, engine: Engine ) + LongParameterList:ToolbarIntegration.kt$ToolbarIntegration$( context: Context, toolbar: BrowserToolbar, toolbarMenu: ToolbarMenu, sessionId: String?, isPrivate: Boolean, renderStyle: ToolbarFeature.RenderStyle ) + LongParameterList:ToolbarView.kt$ToolbarView$( private val context: Context, private val interactor: ToolbarInteractor, private val historyStorage: HistoryStorage?, private val isPrivate: Boolean, val view: BrowserToolbar, engine: Engine ) + LongParameterList:UseCases.kt$UseCases$( private val context: Context, private val engine: Engine, private val sessionManager: SessionManager, private val store: BrowserStore, private val searchEngineManager: SearchEngineManager, private val shortcutManager: WebAppShortcutManager ) + + diff --git a/config/detekt.yml b/config/detekt.yml index 99697f9c5..c8a649a28 100644 --- a/config/detekt.yml +++ b/config/detekt.yml @@ -23,10 +23,6 @@ console-reports: # - 'NotificationReport' # - 'FindingsReport' # - 'BuildFailureReport' - -console-reports: - active: true - exclude: # - 'HtmlOutputReport' - 'PlainOutputReport' - 'XmlOutputReport' @@ -34,6 +30,8 @@ console-reports: comments: active: true excludes: "**/*Test.kt, **/*Spec.kt, **/test/**, **/androidTest/**" + AbsentOrWrongFileLicense: + active: true CommentOverPrivateFunction: active: false CommentOverPrivateProperty: diff --git a/config/license.template b/config/license.template new file mode 100644 index 000000000..e0032240a --- /dev/null +++ b/config/license.template @@ -0,0 +1,3 @@ +/* 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/. */ From aeabd3e91b4cff9babc7b434cdd9d85c3fac736c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakk=C4=B1=20Kaan=20=C3=87al=C4=B1=C5=9Fkan?= Date: Tue, 21 Jul 2020 01:33:32 +0300 Subject: [PATCH 02/31] For #12532: Quick fix for radio button alignment (#12691) --- ...t_manage_site_permissions_exceptions_feature_phone.xml | 4 ++-- .../fragment_manage_site_permissions_feature_phone.xml | 8 ++++---- app/src/main/res/values/dimens.xml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/layout/fragment_manage_site_permissions_exceptions_feature_phone.xml b/app/src/main/res/layout/fragment_manage_site_permissions_exceptions_feature_phone.xml index 7f5f71e4a..5da0ca90d 100644 --- a/app/src/main/res/layout/fragment_manage_site_permissions_exceptions_feature_phone.xml +++ b/app/src/main/res/layout/fragment_manage_site_permissions_exceptions_feature_phone.xml @@ -25,7 +25,7 @@ android:background="?android:attr/selectableItemBackground" android:button="@null" app:drawableStartCompat="?android:attr/listChoiceIndicatorSingle" - android:drawablePadding="@dimen/preference_seek_bar_padding" + android:drawablePadding="@dimen/radio_button_preference_drawable_padding" android:paddingTop="@dimen/radio_button_preference_vertical" android:paddingStart="@dimen/radio_button_preference_horizontal" android:paddingEnd="@dimen/radio_button_preference_horizontal" @@ -40,7 +40,7 @@ android:background="?android:attr/selectableItemBackground" android:button="@null" app:drawableStartCompat="?android:attr/listChoiceIndicatorSingle" - android:drawablePadding="@dimen/preference_seek_bar_padding" + android:drawablePadding="@dimen/radio_button_preference_drawable_padding" android:paddingTop="@dimen/radio_button_preference_vertical" android:paddingStart="@dimen/radio_button_preference_horizontal" android:paddingEnd="@dimen/radio_button_preference_horizontal" diff --git a/app/src/main/res/layout/fragment_manage_site_permissions_feature_phone.xml b/app/src/main/res/layout/fragment_manage_site_permissions_feature_phone.xml index 862a5ad6a..f1019b488 100644 --- a/app/src/main/res/layout/fragment_manage_site_permissions_feature_phone.xml +++ b/app/src/main/res/layout/fragment_manage_site_permissions_feature_phone.xml @@ -24,7 +24,7 @@ android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" android:button="@null" - android:drawablePadding="@dimen/preference_seek_bar_padding" + android:drawablePadding="@dimen/radio_button_preference_drawable_padding" android:paddingStart="@dimen/radio_button_preference_horizontal" android:paddingTop="@dimen/radio_button_preference_vertical" android:paddingEnd="@dimen/radio_button_preference_horizontal" @@ -39,7 +39,7 @@ android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" android:button="@null" - android:drawablePadding="@dimen/preference_seek_bar_padding" + android:drawablePadding="@dimen/radio_button_preference_drawable_padding" android:paddingStart="@dimen/radio_button_preference_horizontal" android:paddingTop="@dimen/radio_button_preference_vertical" android:paddingEnd="@dimen/radio_button_preference_horizontal" @@ -54,7 +54,7 @@ android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" android:button="@null" - android:drawablePadding="@dimen/preference_seek_bar_padding" + android:drawablePadding="@dimen/radio_button_preference_drawable_padding" android:paddingStart="@dimen/radio_button_preference_horizontal" android:paddingTop="@dimen/radio_button_preference_vertical" android:paddingEnd="@dimen/radio_button_preference_horizontal" @@ -68,7 +68,7 @@ android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" android:button="@null" - android:drawablePadding="@dimen/preference_seek_bar_padding" + android:drawablePadding="@dimen/radio_button_preference_drawable_padding" android:paddingStart="@dimen/radio_button_preference_horizontal" android:paddingTop="@dimen/radio_button_preference_vertical" android:paddingEnd="@dimen/radio_button_preference_horizontal" diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 77620233e..290b57d36 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -28,7 +28,7 @@ 12dp 48dp 16dp - 16dp + 24dp 12dp 14sp 18sp From 17cfbc39470de094f5329f2368b6fe2484daa353 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Mon, 20 Jul 2020 15:36:21 -0700 Subject: [PATCH 03/31] No issue: link to Configure Java guide in README; remove NDK requirement. (#12753) I don't have the NDK installed but I can build Fenix just fine. --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ad6224826..f83172937 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,9 @@ Before you attempt to make a contribution please read the [Community Participati ## Build Instructions -Note: Both Android SDK and NDK are required. +Pre-requisites: +* Android SDK +* To run command line tools, you'll need to configure Java: see [our how-to guide](https://github.com/mozilla-mobile/shared-docs/blob/master/android/configure_java.md). 1. Clone or Download the repository: @@ -131,6 +133,9 @@ recommend you use our provided pre-push hook in `config/pre-push-recommended.sh` Using this hook will guarantee your hook gets updated as the repository changes. This hook tries to run as much as possible without taking too much time. +Before you can run the hook, you'll need to configure Java properly because it relies on command line tools: see +[our how-to guide](https://github.com/mozilla-mobile/shared-docs/blob/master/android/configure_java.md). + To add it on Mac/Linux, run this command from the project root: ```sh ln -s ../../config/pre-push-recommended.sh .git/hooks/pre-push From f73411b924689a49c84bc18c43a5087b3dfd2967 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Mon, 20 Jul 2020 16:00:57 -0700 Subject: [PATCH 04/31] For #5499 - Unify CFR drawables and dimens (#12749) --- .../TrackingProtectionOverlay.kt | 4 ++-- app/src/main/res/drawable/ic_cfr_triangle.xml | 16 +++++++++++++++ app/src/main/res/drawable/ic_pbm_triangle.xml | 20 ------------------- app/src/main/res/drawable/ic_triangle.xml | 20 ------------------- .../main/res/layout/pbm_shortcut_popup.xml | 9 +++++---- app/src/main/res/layout/search_widget_cfr.xml | 12 ++++++----- .../tracking_protection_onboarding_popup.xml | 20 ++++++++++--------- app/src/main/res/values/dimens.xml | 7 ++++--- 8 files changed, 45 insertions(+), 63 deletions(-) create mode 100644 app/src/main/res/drawable/ic_cfr_triangle.xml delete mode 100644 app/src/main/res/drawable/ic_pbm_triangle.xml delete mode 100644 app/src/main/res/drawable/ic_triangle.xml diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt index e3839c5d3..0f0dae3dc 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt @@ -81,8 +81,8 @@ class TrackingProtectionOverlay( } val res = context.resources - val triangleWidthPx = res.getDimension(R.dimen.tp_onboarding_triangle_height) - val triangleMarginStartPx = res.getDimension(R.dimen.tp_onboarding_triangle_margin_start) + val triangleWidthPx = res.getDimension(R.dimen.cfr_triangle_height) + val triangleMarginStartPx = res.getDimension(R.dimen.cfr_triangle_margin_edge) val toolbar = getToolbar() val trackingProtectionIcon: View = diff --git a/app/src/main/res/drawable/ic_cfr_triangle.xml b/app/src/main/res/drawable/ic_cfr_triangle.xml new file mode 100644 index 000000000..152e7c372 --- /dev/null +++ b/app/src/main/res/drawable/ic_cfr_triangle.xml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_pbm_triangle.xml b/app/src/main/res/drawable/ic_pbm_triangle.xml deleted file mode 100644 index 025ce0cd9..000000000 --- a/app/src/main/res/drawable/ic_pbm_triangle.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_triangle.xml b/app/src/main/res/drawable/ic_triangle.xml deleted file mode 100644 index d7fd2b45a..000000000 --- a/app/src/main/res/drawable/ic_triangle.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/layout/pbm_shortcut_popup.xml b/app/src/main/res/layout/pbm_shortcut_popup.xml index 53246eece..f365d3681 100644 --- a/app/src/main/res/layout/pbm_shortcut_popup.xml +++ b/app/src/main/res/layout/pbm_shortcut_popup.xml @@ -9,12 +9,13 @@ android:orientation="vertical"> + app:srcCompat="@drawable/ic_cfr_triangle" + app:tint="#7542E5" /> diff --git a/app/src/main/res/layout/tracking_protection_onboarding_popup.xml b/app/src/main/res/layout/tracking_protection_onboarding_popup.xml index 06767c384..6ed17f725 100644 --- a/app/src/main/res/layout/tracking_protection_onboarding_popup.xml +++ b/app/src/main/res/layout/tracking_protection_onboarding_popup.xml @@ -11,12 +11,13 @@ @@ -60,11 +61,12 @@ diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 290b57d36..aa8b61113 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -42,9 +42,10 @@ 48dp 8dp 256dp - 16dp - 16dp - 16dp + + 16dp + 16dp + 16dp 24dp From db6e614d115f755cd9c2ef7fb6815e2ad234cad0 Mon Sep 17 00:00:00 2001 From: liuche Date: Mon, 20 Jul 2020 16:09:18 -0700 Subject: [PATCH 05/31] For #12240: Enable btime youtube-playback Fenix tests (#12760) Co-authored-by: alexandru.ionescu --- taskcluster/ci/browsertime/kind.yml | 12 ++++++++++++ .../fenix_taskgraph/transforms/browsertime.py | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/taskcluster/ci/browsertime/kind.yml b/taskcluster/ci/browsertime/kind.yml index 29a04af0b..253e74e0e 100644 --- a/taskcluster/ci/browsertime/kind.yml +++ b/taskcluster/ci/browsertime/kind.yml @@ -232,3 +232,15 @@ jobs: test-name: google-search-restaurants treeherder: symbol: 'Btime(tp6m-28-c)' + + youtube-playback: + test-name: youtube-playback + run-visual-metrics: False + treeherder: + symbol: 'Btime(ytp)' + args: + by-abi: + # Bug 1558456 - Stop tracking youtube-playback-test on motoG5 for >1080p cases + armeabi-v7a: + - '--test-url-params=exclude=1,2,9,10,17,18,21,22,26,28,30,32,39,40,47,48,55,56,63,64,71,72,79,80,83,84,89,90,95,96' + default: [] diff --git a/taskcluster/fenix_taskgraph/transforms/browsertime.py b/taskcluster/fenix_taskgraph/transforms/browsertime.py index f3b994b87..02fefa48c 100644 --- a/taskcluster/fenix_taskgraph/transforms/browsertime.py +++ b/taskcluster/fenix_taskgraph/transforms/browsertime.py @@ -94,8 +94,14 @@ def build_browsertime_task(config, tasks): run_visual_metrics = task.pop("run-visual-metrics", False) if run_visual_metrics: task["run"]["command"].append("--browsertime-video") + task["run"]["command"].append("--browsertime-no-ffwindowrecorder") task["attributes"]["run-visual-metrics"] = True + # taskcluster is merging task attributes with the default ones + # resulting the --cold extra option in the ytp warm tasks + if 'youtube-playback' in task["name"]: + task["run"]["command"].remove("--cold") + yield task From c94bdc4267bb8f5ea0b6f7347c1344f26cd708ed Mon Sep 17 00:00:00 2001 From: Kainalu Hagiwara Date: Mon, 20 Jul 2020 15:21:45 -0700 Subject: [PATCH 06/31] Revert "For #7104 - Update locale list for release. (#12740)" This reverts commit 4fea8b31ed95166b5fe705fc9cf70df3071365fd. --- l10n.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/l10n.toml b/l10n.toml index aef77e7c5..208d698a0 100644 --- a/l10n.toml +++ b/l10n.toml @@ -7,6 +7,7 @@ locales = [ "ast", "az", "be", + "bg", "bn", "br", "bs", @@ -58,10 +59,13 @@ locales = [ "lt", "ml", "mr", + "ms", "my", "nb-NO", + "ne-NP", "nl", "nn-NO", + "nv", "oc", "pa-IN", "pl", @@ -83,6 +87,7 @@ locales = [ "trs", "uk", "ur", + "uz", "vec", "vi", "zh-CN", From 284ec7b58f3e2e494719d651ab0d2edf736c0a36 Mon Sep 17 00:00:00 2001 From: Sawyer Blatz Date: Fri, 10 Jul 2020 11:07:20 -0700 Subject: [PATCH 07/31] For #12461: Add tab count to tab tray --- .../org/mozilla/fenix/tabtray/TabTrayView.kt | 3 ++ .../main/res/layout/component_tabstray.xml | 25 +++++++------- .../main/res/layout/tabs_tray_tab_counter.xml | 34 +++++++++++++++++++ 3 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 app/src/main/res/layout/tabs_tray_tab_counter.xml 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 b9acc4858..7177b2fad 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt @@ -18,6 +18,7 @@ import com.google.android.material.tabs.TabLayout import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.component_tabstray.view.* import kotlinx.android.synthetic.main.component_tabstray_fab.view.* +import kotlinx.android.synthetic.main.tabs_tray_tab_counter.* import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -245,6 +246,8 @@ class TabTrayView( View.VISIBLE } view.tab_tray_overflow.isVisible = !hasNoTabs + + counter_text.text = "${state.normalTabs.size}" } } diff --git a/app/src/main/res/layout/component_tabstray.xml b/app/src/main/res/layout/component_tabstray.xml index 132d86363..51a1edc62 100644 --- a/app/src/main/res/layout/component_tabstray.xml +++ b/app/src/main/res/layout/component_tabstray.xml @@ -46,27 +46,28 @@ android:layout_width="0dp" android:layout_height="80dp" android:background="@color/foundation_normal_theme" - app:tabIndicatorColor="@color/accent_normal_theme" - app:tabIconTint="@color/tab_icon" - app:tabRippleColor="@android:color/transparent" - app:tabGravity="fill" - app:layout_constraintWidth_percent="0.5" + app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/handle" - app:layout_constraintStart_toStartOf="parent"> + app:layout_constraintWidth_percent="0.5" + app:tabGravity="fill" + app:tabIconTint="@color/tab_icon" + app:tabIndicatorColor="@color/accent_normal_theme" + app:tabRippleColor="@android:color/transparent"> + android:layout_height="match_parent" + android:contentDescription="@string/tab_header_label" + android:layout="@layout/tabs_tray_tab_counter" + app:tabIconTint="@color/tab_icon" /> + android:layout_height="match_parent" + android:contentDescription="@string/tabs_header_private_tabs_title" + android:icon="@drawable/ic_private_browsing" /> diff --git a/app/src/main/res/layout/tabs_tray_tab_counter.xml b/app/src/main/res/layout/tabs_tray_tab_counter.xml new file mode 100644 index 000000000..df8b697ac --- /dev/null +++ b/app/src/main/res/layout/tabs_tray_tab_counter.xml @@ -0,0 +1,34 @@ + + + + + + + + + \ No newline at end of file From 02a929a926153dbe1dcb2c5f4dac6ccdcb5882bf Mon Sep 17 00:00:00 2001 From: Kainalu Hagiwara Date: Mon, 20 Jul 2020 15:51:43 -0700 Subject: [PATCH 08/31] No issue - Suppress new warnings from detekt update --- app/src/main/java/org/mozilla/fenix/collections/TabDiffUtil.kt | 1 + .../fenix/home/sessioncontrol/SessionControlController.kt | 2 +- .../org/mozilla/fenix/library/bookmarks/BookmarkController.kt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/collections/TabDiffUtil.kt b/app/src/main/java/org/mozilla/fenix/collections/TabDiffUtil.kt index a555dd8f0..35c87c9b6 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/TabDiffUtil.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/TabDiffUtil.kt @@ -10,6 +10,7 @@ import org.mozilla.fenix.home.Tab /** * Diff callback for comparing tab lists with selected state. */ +@Suppress("LongParameterList") internal class TabDiffUtil( private val old: List, private val new: List, 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 2935fd075..2cf7a5471 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 @@ -128,7 +128,7 @@ interface SessionControlController { fun handleCreateCollection() } -@SuppressWarnings("TooManyFunctions", "LargeClass") +@SuppressWarnings("TooManyFunctions", "LargeClass", "LongParameterList") class DefaultSessionControlController( private val activity: HomeActivity, private val engine: Engine, diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkController.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkController.kt index 1aca89890..f8503653e 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkController.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkController.kt @@ -47,7 +47,7 @@ interface BookmarkController { fun handleBackPressed() } -@SuppressWarnings("TooManyFunctions") +@SuppressWarnings("TooManyFunctions", "LongParameterList") class DefaultBookmarkController( private val activity: HomeActivity, private val navController: NavController, From 41c256e4749e053366afa0f9d37cfad1b5decb33 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Mon, 20 Jul 2020 18:09:01 -0700 Subject: [PATCH 09/31] Fixes #12752, #12764 - Temp disable openHistoryInNewTabTest intermittent UI test failures (#12754) * Fixes #12752 - Temp disable intermittent failing test --- app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt | 1 + app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt | 2 ++ .../java/org/mozilla/fenix/ui/MediaNotificationTest.kt | 2 ++ .../androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt | 1 + 4 files changed, 6 insertions(+) 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 14f91034d..8ab1a1b0e 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt @@ -322,6 +322,7 @@ class BookmarksTest { } } + @Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/12752") @Test fun openSelectionInNewTabTest() { val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) 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 db54d7480..9abc735aa 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt @@ -11,6 +11,7 @@ import mozilla.components.browser.storage.sync.PlacesHistoryStorage 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.helpers.AndroidAssetDispatcher @@ -203,6 +204,7 @@ class HistoryTest { } } + @Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/12752") @Test fun openHistoryInNewTabTest() { val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) 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 1ac67e6e5..386c95bb9 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt @@ -8,6 +8,7 @@ import androidx.test.uiautomator.UiSelector 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.helpers.AndroidAssetDispatcher @@ -135,6 +136,7 @@ class MediaNotificationTest { } } + @Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/12764") @Test fun mediaSystemNotificationInPrivateModeTest() { val audioTestPage = TestAssetHelper.getAudioPageAsset(mockWebServer) 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 19178cce3..9879ace57 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt @@ -104,6 +104,7 @@ class TabbedBrowsingTest { } } + @Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/12752") @Test fun closeAllTabsTest() { val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) From 80b3b2dd82b07bc6afadc6a62d5aaf9d20af5123 Mon Sep 17 00:00:00 2001 From: mozilla-l10n-automation-bot <54512241+mozilla-l10n-automation-bot@users.noreply.github.com> Date: Mon, 20 Jul 2020 21:26:00 -0400 Subject: [PATCH 10/31] Import l10n. (#12766) --- app/src/main/res/values-br/strings.xml | 18 ++-- app/src/main/res/values-mr/strings.xml | 16 +++ app/src/main/res/values-ro/strings.xml | 136 ++++++++++++++++++++++++- 3 files changed, 159 insertions(+), 11 deletions(-) diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml index 601d79536..6c0ea763f 100644 --- a/app/src/main/res/values-br/strings.xml +++ b/app/src/main/res/values-br/strings.xml @@ -78,7 +78,7 @@ Askouezhioù - Nʼeus tamm enlugellad ebet amañ + Nʼeus tamm askouezh ebet amañ Skoazell @@ -296,7 +296,7 @@ Anv an trevnad nʼhall ket bezañ goullo. - O goubredañ... + O c’houbredañ... Goubredañ cʼhwitet. Berzh diwezhañ: %s @@ -390,7 +390,7 @@ Krecʼh - Diaz + Traoñ @@ -398,7 +398,7 @@ Teñval - Lakaet gant an esperner tredan + Dibabet gant an esperner tredan Mont gant neuz ar benveg @@ -474,7 +474,7 @@ Rannañ an ivinelloù - Enrollañ an ivinelloù en dastumad + Enrollañ an ivinelloù en un dastumad Lañser an ivinell @@ -690,7 +690,7 @@ Lañser an dastumadoù - Dastumit ar pezh a gont evidocʼh + Dastumit ar pezh a zo pouezus evidoc‘h Strollit ar cʼhlaskoù, al lecʼhiennoù hag an ivinelloù heñvel evit mont daveto buanocʼh. @@ -863,7 +863,7 @@ Skeudennoù ha restroù er c’hrubuilh - Dieubiñ a ra egor kadaviñ + Dieubiñ a raio egor kadaviñ Aotreoù al lec’hienn @@ -1170,7 +1170,7 @@ URL eilet er golver - Ouzhpennañ dʼar skramm degemer + Lakaat er skramm degemer Nullañ @@ -1365,7 +1365,7 @@ Ha fellout a ra deocʼh dilemel ar sined-mañ ? - Ouzhpennañ dʼal lecʼhiennoù gwellañ + Lakaat el lecʼhiennoù gwellañ Gwiriet gant:%1$s diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index e752cf54f..b8f5e4c14 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -919,6 +919,20 @@ Firefox Nightly हलले आहे + + या अॅपला यापुढे सुरक्षा अद्यतने प्राप्त होणार नाहीत. हे अॅप वापरणे थांबवा आणि नवीन Nightly वर स्विच करा. + \n\nआपल्या वाचनखुणा, लॉगिन, आणि इतिहास इतर अॅप वर स्थानांतरित करण्यासाठी, एक Firefox खाते त.ार करा. + + नवीन Nightly वर स्विच करा + + + Firefox Nightly हलले आहे + + या अॅपला यापुढे सुरक्षा अद्यतने प्राप्त होणार नाहीत. नवीन Nightly मिळवा आणि हे अॅप वापरणे थांबवा. + \n\nआपल्या वाचनखुणा, लॉगिन, आणि इतिहास इतर अॅप वर स्थानांतरित करण्यासाठी, एक Firefox खाते त.ार करा. + + नवीन Nightly मिळवा + @@ -954,6 +968,8 @@ सिंक चालू आहे साइन इन करण्यात अयशस्वी + + स्वयंचलित गोपनीयता कठोर (सुचवलेले) Filele deschise vor fi afișate aici. + + Aici vor fi afișate filele tale private. + + 1 filă deschisă. Atinge pentru a comuta filele. + + %1$s file deschise. Atinge pentru a comuta filele. + %1$s este realizat de Mozilla. @@ -38,6 +45,9 @@ Nu, mulțumesc + + + Accesează Firefox mai repede. Adaugă un widget pe ecranul de start. Adaugă widget @@ -110,6 +120,8 @@ Cu tehnologie %1$s Mod de lectură + + Închide modul de lectură Deschide în aplicație @@ -251,6 +263,8 @@ Afișează comenzile rapide pentru căutări Afișează sugestii de căutare + + Afișează căutarea vocală Afișează în sesiuni private @@ -367,6 +381,9 @@ Șterge contul + + + firefox.com/pair]]> Cameră deschisă @@ -408,6 +425,8 @@ Istoric + + File sincronizate Listă de lectură @@ -430,6 +449,24 @@ Adaugă fila Adaugă filă privată + + Privată + + File deschise + + Salvează în colecție + + Partajează toate filele + + Închide toate filele + + Filă nouă + + Acasă + + Mod de comutare a filelor + + Elimină fila din colecție Închide fila @@ -440,6 +477,8 @@ Închide toate filele Partajează filele + + Salvează filele în colecție Meniu de file @@ -463,6 +502,9 @@ Șterge + + %1$s (mod privat) + Șterge istoricul @@ -652,6 +694,10 @@ Meniu de colecții + + Colectează ce te interesează + + Grupează căutările similare, site-urile și filele pentru acces mai rapid ulterior. Selectează filele @@ -681,6 +727,9 @@ Salvează + + Afișează + Colecția %d @@ -791,11 +840,15 @@ Dimensiunea fontului + + Mărime automată a fontului Dimensiunea fontului va coincide cu cea din setările Android. Dezactivează pentru a gestiona aici dimensiunea fontului. Șterge datele de navigare + + File deschise %d (de) file @@ -827,8 +880,6 @@ Șterge automat datele de navigare când selectezi „Închide” în meniul principal Șterge automat datele de navigare când selectezi \„Închide\” în meniul principal - - Istoric de navigare Închide @@ -852,6 +903,9 @@ Firefox Nightly se actualizează în fiecare seară și are funcționalități experimentale noi Dar poate fi mai puțin stabil. Descarcă browserul nostru beta pentru o experiență mai stabilă. + + Descarcă Firefox Beta pentru Android + Firefox Nightly s-a mutat @@ -903,8 +957,21 @@ Sync este activat Eroare de autentificare + + Confidențialitate automată + + Setările de confidențialitate și securitate blochează elementele de urmărire, softurile rău-intenționate și firmele care te urmăresc. + + Standard (implicit) + + Blochează mai puține elemente de urmărire. Paginile se vor încărca normal. Strictă (recomandat) + + Strictă + + Blochează mai multe elemente de urmărire, reclame și ferestre pop-up. Paginile se încarcă mai repede, dar se poate ca unele funcționalități să nu meargă. @@ -991,14 +1058,22 @@ Păstrează-ți datele pentru tine. %s te protejează de multe dintre cele mai frecvente elemente de urmărire care monitorizează ce faci online. Află mai multe + + Standard (implicit) + + Blochează mai puține elemente de urmărire. Paginile se vor încărca normal. Ce blochează protecția standard împotriva urmăririi Strictă + + Blochează mai multe elemente de urmărire, reclame și ferestre pop-up. Paginile se încarcă mai repede, dar se poate ca unele funcționalități să nu meargă. Ce blochează protecția strictă împotriva urmăririi Personalizat + + Alege ce elemente de urmărire și scripturi să blochezi. Ce blochează protecția personalizată împotriva urmăririi @@ -1048,6 +1123,8 @@ Oprește încărcarea reclamelor, videoclipurilor și a altor conținuturi externe care conțin coduri de urmărire. Poate afecta anumite funcționalități ale site-ului. + + Când scutul este violet, %s a blocat elemente de urmărire de pe un site. Atinge pentru mai multe informații. Protecțiile sunt ACTIVE pentru acest site @@ -1069,6 +1146,8 @@ Asistență + + Defecțiuni Politică de confidențialitate @@ -1204,6 +1283,13 @@ Activează permiterea apropierii/depărtării cu degetele pentru zoom, chiar și pe site-uri web care împiedică acest gest. + + Denumire (A-Z) + + Folosite ultima dată + + Meniu de sortare a datelor de autentificare + Adaugă un motor de căutare @@ -1296,11 +1382,57 @@ Verificat de: %1$s Șterge + + Editează Sigur vrei să ștergi aceste date de autentificare? Șterge + + Opțiuni de autentificare + + Câmpul de text editabil pentru adresa web a datelor de autentificare. + + Câmpul de text editabil pentru numele de utilizator al datelor de autentificare. + + Câmp de text editabil pentru parola datelor de autentificare. + + Salvează modificările datelor de autentificare. + + Renunță la modificări + + Editează + + Necesită o parolă + + Căutare vocală + + Vorbește acum + + Există un set de date de autentificare cu acest nume de utilizator + + + + Conectare cu un cont Firefox. + + Conectează alt dispozitiv. + + Te rugăm să te autentifici din nou. + + Te rugăm să activezi sincronizarea filelor. + + Nu ai nicio filă deschisă în Firefox pe celelalte dispozitive. + + Afișează o listă de file de pe celelalte dispozitive. + + Autentifică-te în Sync + + + + A fost atinsă limita de site-uri de top + + Pentru a adăuga un site de top nou, trebuie să elimini unul existent. Ține degetul pe denumirea site-ului pentru selectare și apoi atinge „Elimină”. Ok, am înțeles From b31564d5fc3c2df25ed50ec8a5598b03d6f956fb Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Tue, 21 Jul 2020 03:55:41 +0200 Subject: [PATCH 11/31] Remove the duplicated/outdated 'activation' ping docs (#12421) This additionally overhauls the Fenix telemetry docs to point at the Glean autogenerated docs, to prevent documentation getting outdated again. --- docs/activation.md | 36 ------------------------------------ docs/telemetry.md | 17 +++-------------- 2 files changed, 3 insertions(+), 50 deletions(-) delete mode 100644 docs/activation.md diff --git a/docs/activation.md b/docs/activation.md deleted file mode 100644 index 9c05c1755..000000000 --- a/docs/activation.md +++ /dev/null @@ -1,36 +0,0 @@ -# The `activation` ping - -## Description -This ping provides a measure of the activation of mobile products. - -## Scheduling -The `activation` ping is automatically sent at the very first startup, after Glean is initialized. -It is only sent once and only re-attempted a subsequent startups if it hasn't been sent yet. - -## Contents -This ping contains the following fields: - -| Field name | Type | Description | -|---|---|---| -| `identifier` | String | An hashed and salted version of the Google Advertising ID from the device. | -| `activation_id` | UUID | An alternate identifier, not correlated with the client_id, generated once and only sent with the activation ping. | - -The `activation` ping also includes the common [ping sections](https://github.com/mozilla-mobile/android-components/blob/master/components/service/glean/docs/pings/pings.md#ping-sections) -found in all pings, with the exclusion of the `client_id` (as defined by the [`pings.yaml`](../app/pings.yaml) file). - -## Example `activation` ping - -```json -{ - "ping_info": { }, - "client_info": { }, - "metrics": { - "string": { - "activation.identifier": "d+lnddDYN2ILBDGvhBIBHORRMrmVwTCp6rGLLFi8SMo=" - }, - "uuid": { - "activation.activation_id": "c0c40a5f-bd75-41ca-8097-9a38103de7fe" - } - } -} -``` diff --git a/docs/telemetry.md b/docs/telemetry.md index a98347111..fb73dfc4a 100644 --- a/docs/telemetry.md +++ b/docs/telemetry.md @@ -2,21 +2,10 @@ Fenix uses Mozilla's telemetry service (Glean) and LeanPlum to measure feature performance and engagement. -## Baseline ping +## Glean pings and metrics +By using the Glean SDK, Fenix can send the pings the SDK owns and defines, as documented [in the Glean SDK docs](https://mozilla.github.io/glean/book/user/pings/index.html). -Fenix creates and tries to send a "baseline" ping when the app goes to the background. This baseline ping is defined by the [Glean](https://github.com/mozilla/glean/tree/master/docs/user/pings) component and [documented in the Android Components repository](https://github.com/mozilla/glean/blob/master/docs/user/pings/baseline.md). - -## Metrics ping - -Fenix creates and tries to send a "baseline" ping. It is defined inside the [`metrics.yaml`](https://github.com/mozilla-mobile/fenix/blob/master/app/metrics.yaml) file. This ping includes things like whether or not Fenix is currently the default browser. - -## Events - -Fenix sends event pings that allows us to measure feature performance. These are defined inside the [`metrics.yaml`](https://github.com/mozilla-mobile/fenix/blob/master/app/metrics.yaml) file. - -## Activation - -Fenix sends an activation ping once, at startup. Further documentation can be found in the [`activation` ping](activation.md) docs. +Additional metrics or pings defined by Fenix are documented in the [Glean SDK autogenerated docs](metrics.md). ## Leanplum See [here](https://github.com/mozilla-mobile/fenix/blob/master/docs/mma.md) for details on Leanplum usage in Firefox Preview. From 98d5ae6b899cab830f6b53fa103230f4c74ea0c4 Mon Sep 17 00:00:00 2001 From: Kainalu Hagiwara Date: Mon, 20 Jul 2020 16:26:43 -0700 Subject: [PATCH 12/31] For #12759 - Keep a separate list of release locales. --- automation/taskcluster/l10n/locales.py | 4 +- l10n.toml | 92 ++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/automation/taskcluster/l10n/locales.py b/automation/taskcluster/l10n/locales.py index 5e2e80fe1..80c87bc2c 100644 --- a/automation/taskcluster/l10n/locales.py +++ b/automation/taskcluster/l10n/locales.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, print_function, unicode_literals import re -OPEN_LOCALES = "locales = [" +OPEN_LOCALES = "release_locales = [" CLOSE_LOCALES = "]" def trim_to_locale(str): @@ -42,7 +42,7 @@ def get_release_locales(): for line in file: if line == OPEN_LOCALES: locales_opened = True - elif line == CLOSE_LOCALES: + elif line == CLOSE_LOCALES and locales_opened == True: locales_closed = True break elif locales_opened: diff --git a/l10n.toml b/l10n.toml index 208d698a0..bb0d019f2 100644 --- a/l10n.toml +++ b/l10n.toml @@ -1,6 +1,7 @@ basepath = "." +# Locales that should be present in non-release builds and on Pontoon locales = [ "an", "ar", @@ -94,6 +95,97 @@ locales = [ "zh-TW", ] +# Locales that should be present in release builds +# Locales that are at 70% or higher completion on https://pontoon.mozilla.org/projects/android-l10n/ +# should be in this list +release_locales = [ + "an", + "ar", + "ast", + "az", + "be", + "bn", + "br", + "bs", + "ca", + "cak", + "co", + "cs", + "cy", + "da", + "de", + "dsb", + "el", + "en-CA", + "en-GB", + "eo", + "es", + "es-AR", + "es-CL", + "es-ES", + "es-MX", + "et", + "eu", + "fa", + "ff", + "fi", + "fr", + "fy-NL", + "ga-IE", + "gd", + "gn", + "gu-IN", + "he", + "hi-IN", + "hr", + "hsb", + "hu", + "hy-AM", + "id", + "is", + "it", + "ja", + "ka", + "kab", + "kk", + "kn", + "ko", + "lij", + "lo", + "lt", + "ml", + "mr", + "my", + "nb-NO", + "nl", + "nn-NO", + "oc", + "pa-IN", + "pl", + "pt-BR", + "pt-PT", + "rm", + "ro", + "ru", + "sk", + "sl", + "sq", + "sr", + "su", + "sv-SE", + "ta", + "te", + "th", + "tr", + "trs", + "uk", + "ur", + "vec", + "vi", + "zh-CN", + "zh-TW", +] + # Expose the following branches to localization # Changes to this list should be announced to the l10n team ahead of time. branches = [ From e809df08583b7df41064f2876c0a1ffffb836961 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Mon, 20 Jul 2020 19:06:22 -0700 Subject: [PATCH 13/31] Pass settings and metrics to CFR --- .../mozilla/fenix/browser/BrowserFragment.kt | 3 +- .../org/mozilla/fenix/cfr/SearchWidgetCFR.kt | 39 ++++++++++--------- .../org/mozilla/fenix/home/HomeFragment.kt | 8 +++- .../TrackingProtectionOverlay.kt | 10 +++-- .../TrackingProtectionOverlayTest.kt | 23 +++++------ 5 files changed, 47 insertions(+), 36 deletions(-) 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 5c6bc0492..4c3b5882b 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -150,7 +150,8 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { val toolbarSessionObserver = TrackingProtectionOverlay( context = context, - settings = settings + settings = settings, + metrics = context.components.analytics.metrics ) { browserToolbarView.view } diff --git a/app/src/main/java/org/mozilla/fenix/cfr/SearchWidgetCFR.kt b/app/src/main/java/org/mozilla/fenix/cfr/SearchWidgetCFR.kt index 1b178bc01..1bbe94e05 100644 --- a/app/src/main/java/org/mozilla/fenix/cfr/SearchWidgetCFR.kt +++ b/app/src/main/java/org/mozilla/fenix/cfr/SearchWidgetCFR.kt @@ -20,8 +20,7 @@ import kotlinx.android.synthetic.main.tracking_protection_onboarding_popup.view. import org.mozilla.fenix.R import org.mozilla.fenix.components.SearchWidgetCreator import org.mozilla.fenix.components.metrics.Event -import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.utils.Settings /** @@ -29,27 +28,29 @@ import org.mozilla.fenix.utils.Settings */ class SearchWidgetCFR( private val context: Context, + private val settings: Settings, + private val metrics: MetricController, private val getToolbar: () -> View ) { fun displayIfNecessary() { - if (!context.settings().isInSearchWidgetExperiment || - !context.settings().shouldDisplaySearchWidgetCFR() || - isShown - ) { return } - - isShown = true - showSearchWidgetCFR() + if (settings.isInSearchWidgetExperiment && + settings.shouldDisplaySearchWidgetCFR() && + !isShown + ) { + isShown = true + showSearchWidgetCFR() + } } - @Suppress("MagicNumber", "InflateParams") + @Suppress("InflateParams") private fun showSearchWidgetCFR() { - context.settings().incrementSearchWidgetCFRDisplayed() + settings.incrementSearchWidgetCFRDisplayed() val searchWidgetCFRDialog = Dialog(context) val layout = LayoutInflater.from(context) .inflate(R.layout.search_widget_cfr, null) - val isBottomToolbar = Settings.getInstance(context).shouldUseBottomToolbar + val isBottomToolbar = settings.shouldUseBottomToolbar layout.drop_down_triangle.isGone = isBottomToolbar layout.pop_up_triangle.isVisible = isBottomToolbar @@ -63,16 +64,16 @@ class SearchWidgetCFR( } layout.cfr_neg_button.setOnClickListener { - context.components.analytics.metrics.track(Event.SearchWidgetCFRNotNowPressed) + metrics.track(Event.SearchWidgetCFRNotNowPressed) searchWidgetCFRDialog.dismiss() - context.settings().manuallyDismissSearchWidgetCFR() + settings.manuallyDismissSearchWidgetCFR() } layout.cfr_pos_button.setOnClickListener { - context.components.analytics.metrics.track(Event.SearchWidgetCFRAddWidgetPressed) + metrics.track(Event.SearchWidgetCFRAddWidgetPressed) SearchWidgetCreator.createSearchWidget(context) searchWidgetCFRDialog.dismiss() - context.settings().manuallyDismissSearchWidgetCFR() + settings.manuallyDismissSearchWidgetCFR() } searchWidgetCFRDialog.apply { @@ -90,16 +91,16 @@ class SearchWidgetCFR( searchWidgetCFRDialog.setOnCancelListener { isShown = false - context.components.analytics.metrics.track(Event.SearchWidgetCFRCanceled) + metrics.track(Event.SearchWidgetCFRCanceled) } searchWidgetCFRDialog.setOnDismissListener { isShown = false - context.settings().incrementSearchWidgetCFRDismissed() + settings.incrementSearchWidgetCFRDismissed() } searchWidgetCFRDialog.show() - context.components.analytics.metrics.track(Event.SearchWidgetCFRDisplayed) + metrics.track(Event.SearchWidgetCFRDisplayed) } companion object { 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 1740152f1..0f3d75878 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -375,7 +375,13 @@ class HomeFragment : Fragment() { // the CFR in. view.toolbar_wrapper.doOnLayout { if (!browsingModeManager.mode.isPrivate) { - SearchWidgetCFR(view.context) { view.toolbar_wrapper }.displayIfNecessary() + SearchWidgetCFR( + context = view.context, + settings = view.context.settings(), + metrics = view.context.components.analytics.metrics + ) { + view.toolbar_wrapper + }.displayIfNecessary() } } diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt index 0f0dae3dc..194c1326d 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt @@ -21,6 +21,7 @@ import kotlinx.android.synthetic.main.tracking_protection_onboarding_popup.view. import mozilla.components.browser.session.Session import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.increaseTapArea import org.mozilla.fenix.utils.Settings @@ -32,6 +33,7 @@ import org.mozilla.fenix.utils.Settings class TrackingProtectionOverlay( private val context: Context, private val settings: Settings, + private val metrics: MetricController, private val getToolbar: () -> View ) : Session.Observer { @@ -62,7 +64,7 @@ class TrackingProtectionOverlay( val layout = LayoutInflater.from(context) .inflate(R.layout.tracking_protection_onboarding_popup, null) - val isBottomToolbar = Settings.getInstance(context).shouldUseBottomToolbar + val isBottomToolbar = settings.shouldUseBottomToolbar layout.drop_down_triangle.isGone = isBottomToolbar layout.pop_up_triangle.isVisible = isBottomToolbar @@ -76,7 +78,7 @@ class TrackingProtectionOverlay( val closeButton = layout.findViewById(R.id.close_onboarding) closeButton.increaseTapArea(BUTTON_INCREASE_DPS) closeButton.setOnClickListener { - context.components.analytics.metrics.track(Event.ContextualHintETPDismissed) + metrics.track(Event.ContextualHintETPDismissed) trackingOnboardingDialog.dismiss() } @@ -115,12 +117,12 @@ class TrackingProtectionOverlay( val etpShield = getToolbar().findViewById(R.id.mozac_browser_toolbar_tracking_protection_indicator) trackingOnboardingDialog.message.setOnClickListener { - context.components.analytics.metrics.track(Event.ContextualHintETPInsideTap) + metrics.track(Event.ContextualHintETPInsideTap) trackingOnboardingDialog.dismiss() etpShield.performClick() } - context.components.analytics.metrics.track(Event.ContextualHintETPDisplayed) + metrics.track(Event.ContextualHintETPDisplayed) trackingOnboardingDialog.show() settings.incrementTrackingProtectionOnboardingCount() } diff --git a/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlayTest.kt b/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlayTest.kt index e4a9dca79..b408aa945 100644 --- a/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlayTest.kt +++ b/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlayTest.kt @@ -6,7 +6,9 @@ package org.mozilla.fenix.trackingprotection import android.content.Context import android.view.View +import io.mockk.MockKAnnotations import io.mockk.every +import io.mockk.impl.annotations.MockK import io.mockk.mockk import io.mockk.spyk import io.mockk.verify @@ -16,28 +18,27 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.R -import org.mozilla.fenix.utils.Settings +import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.utils.Settings @RunWith(FenixRobolectricTestRunner::class) class TrackingProtectionOverlayTest { private lateinit var context: Context - private lateinit var settings: Settings - private lateinit var toolbar: View - private lateinit var icon: View - private lateinit var session: Session - private lateinit var overlay: TrackingProtectionOverlay + @MockK(relaxed = true) private lateinit var settings: Settings + @MockK(relaxed = true) private lateinit var metrics: MetricController + @MockK(relaxed = true) private lateinit var toolbar: View + @MockK(relaxed = true) private lateinit var icon: View + @MockK(relaxed = true) private lateinit var session: Session + @MockK(relaxed = true) private lateinit var overlay: TrackingProtectionOverlay @Before fun setup() { + MockKAnnotations.init(this) context = spyk(testContext) - settings = mockk(relaxed = true) - toolbar = mockk(relaxed = true) - icon = mockk(relaxed = true) - session = mockk(relaxed = true) - overlay = TrackingProtectionOverlay(context, settings) { toolbar } + overlay = TrackingProtectionOverlay(context, settings, metrics) { toolbar } every { toolbar.findViewById(R.id.mozac_browser_toolbar_tracking_protection_indicator) } returns icon } From 6ab6f3efe9b47d59c50b10c2a4f03e220e099b60 Mon Sep 17 00:00:00 2001 From: Oana Horvath Date: Tue, 21 Jul 2020 15:49:24 +0300 Subject: [PATCH 14/31] Fix UI tests from #12752 #12637 #12764 (#12779) * For #12752 & #12764: Fixes tabs tray behavior in UI tests * For #12637: fixes goBackTest --- .../java/org/mozilla/fenix/ui/BookmarksTest.kt | 6 ++++-- .../java/org/mozilla/fenix/ui/HistoryTest.kt | 6 +++--- .../org/mozilla/fenix/ui/MediaNotificationTest.kt | 4 ++-- .../org/mozilla/fenix/ui/NavigationToolbarTest.kt | 2 ++ .../org/mozilla/fenix/ui/TabbedBrowsingTest.kt | 15 +++++++++++---- .../mozilla/fenix/ui/robots/HomeScreenRobot.kt | 3 +++ .../fenix/ui/robots/ThreeDotMenuMainRobot.kt | 7 +++---- 7 files changed, 28 insertions(+), 15 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 8ab1a1b0e..3bb2f5348 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt @@ -322,7 +322,6 @@ class BookmarksTest { } } - @Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/12752") @Test fun openSelectionInNewTabTest() { val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -331,7 +330,10 @@ class BookmarksTest { createBookmark(defaultWebPage.url) }.openTabDrawer { closeTab() - }.openHomeScreen { }.openThreeDotMenu { + } + + homeScreen { + }.openThreeDotMenu { }.openBookmarks { bookmarksListIdlingResource = RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list)) 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 9abc735aa..c1b65d5f2 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt @@ -11,7 +11,6 @@ import mozilla.components.browser.storage.sync.PlacesHistoryStorage 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.helpers.AndroidAssetDispatcher @@ -204,7 +203,6 @@ class HistoryTest { } } - @Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/12752") @Test fun openHistoryInNewTabTest() { val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -214,7 +212,9 @@ class HistoryTest { mDevice.waitForIdle() }.openTabDrawer { closeTab() - }.openHomeScreen { }.openThreeDotMenu { + } + + homeScreen { }.openThreeDotMenu { }.openHistory { longTapSelectItem(firstWebPage.url) openActionBarOverflowOrOptionsMenu(activityTestRule.activity) 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 386c95bb9..14d463209 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt @@ -136,7 +136,7 @@ class MediaNotificationTest { } } - @Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/12764") + @Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/12645") @Test fun mediaSystemNotificationInPrivateModeTest() { val audioTestPage = TestAssetHelper.getAudioPageAsset(mockWebServer) @@ -160,7 +160,7 @@ class MediaNotificationTest { verifyMediaIsPaused() }.openTabDrawer { closeTab() - }.openHomeScreen { } + } mDevice.openNotification() 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 41b623d73..15f76597c 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt @@ -58,8 +58,10 @@ class NavigationToolbarTest { mDevice.waitForIdle() }.openNavigationToolbar { }.enterURLAndEnterToBrowser(nextWebPage.url) { + mDevice.waitForIdle() verifyUrl(nextWebPage.url.toString()) mDevice.pressBack() + mDevice.waitForIdle() verifyUrl(defaultWebPage.url.toString()) } } 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 9879ace57..3470db229 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt @@ -9,6 +9,7 @@ import androidx.test.uiautomator.UiDevice import okhttp3.mockwebserver.MockWebServer import org.junit.After import org.junit.Before +import org.junit.BeforeClass import org.junit.Ignore import org.junit.Rule import org.junit.Test @@ -53,6 +54,16 @@ class TabbedBrowsingTest { } } + // changing the device preference for Touch and Hold delay, to avoid long-clicks instead of a single-click + companion object { + @BeforeClass + @JvmStatic + fun setDevicePreference() { + val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + mDevice.executeShellCommand("settings put secure long_press_timeout 3000") + } + } + @After fun tearDown() { mockWebServer.shutdown() @@ -104,7 +115,6 @@ class TabbedBrowsingTest { } } - @Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/12752") @Test fun closeAllTabsTest() { val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -119,7 +129,6 @@ class TabbedBrowsingTest { verifySaveCollection() }.closeAllTabs { verifyNoTabsOpened() - }.openHomeScreen { } // Repeat for Private Tabs @@ -135,8 +144,6 @@ class TabbedBrowsingTest { verifyCloseAllTabsButton() }.closeAllTabs { verifyNoTabsOpened() - }.openHomeScreen { - verifyPrivateSessionMessage() } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt index 7299729ff..f1e24be71 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HomeScreenRobot.kt @@ -63,6 +63,7 @@ class HomeScreenRobot { fun verifyHomeToolbar() = assertHomeToolbar() fun verifyHomeComponent() = assertHomeComponent() fun verifyDefaultSearchEngine(searchEngine: String) = verifySearchEngineIcon(searchEngine) + fun verifyNoTabsOpened() = assertNoTabsOpened() // First Run elements fun verifyWelcomeHeader() = assertWelcomeHeader() @@ -529,6 +530,8 @@ private fun assertHomeComponent() = onView(ViewMatchers.withResourceName("sessionControlRecyclerView")) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) +private fun assertNoTabsOpened() = onView(withId(R.id.counter_text)).check(matches(withText("0"))) + private fun threeDotButton() = onView(allOf(withId(R.id.menuButton))) private fun verifySearchEngineIcon(searchEngineIcon: Bitmap, searchEngineName: String) { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt index c1a207379..51665856b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuMainRobot.kt @@ -253,12 +253,11 @@ class ThreeDotMenuMainRobot { return BrowserRobot.Transition() } - fun closeAllTabs(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition { -// mDevice.waitNotNull(Until.findObject(By.text("Close all tabs")), waitingTime) + fun closeAllTabs(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition { closeAllTabsButton().click() - TabDrawerRobot().interact() - return TabDrawerRobot.Transition() + HomeScreenRobot().interact() + return HomeScreenRobot.Transition() } fun openFindInPage(interact: FindInPageRobot.() -> Unit): FindInPageRobot.Transition { From 899f6e63ad6c99582544f3d7044253e37070bee5 Mon Sep 17 00:00:00 2001 From: TejaswiKarasani Date: Tue, 21 Jul 2020 19:03:06 +0530 Subject: [PATCH 15/31] No issue: verify ETP toolbar shield icon is not displayed if ETP is OFF globally in ETP UI tests (#12623) --- .../java/org/mozilla/fenix/ui/SmokeTest.kt | 35 ++++++++++++++++ .../mozilla/fenix/ui/robots/BrowserRobot.kt | 10 +++++ .../mozilla/fenix/ui/robots/SettingsRobot.kt | 1 + ...sSubMenuEnhancedTrackingProtectionRobot.kt | 42 +++++++++++++++++++ 4 files changed, 88 insertions(+) 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 b7d7519ea..2a122ac8d 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt @@ -215,4 +215,39 @@ class SmokeTest { } } } + + @Test + fun verifyETPToolbarShieldIconIsNotDisplayedIfETPIsOFFGloballyTest() { + val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + homeScreen { + }.openThreeDotMenu { + }.openSettings { + }.openEnhancedTrackingProtectionSubMenu { + clickEnhancedTrackingProtectionDefaults() + verifyEnhancedTrackingProtectionOptionsGrayedOut() + }.goBackToHomeScreen { + navigationToolbar { + }.enterURLAndEnterToBrowser(defaultWebPage.url) { + verifyEnhancedTrackingProtectionPanelNotVisible() + }.openThreeDotMenu { + }.clickAddOnsReportSiteIssue { + verifyUrl("webcompat.com/issues/new") + verifyTabCounter("2") + }.openTabDrawer { + }.openHomeScreen { + }.openThreeDotMenu { + }.openSettings { + }.openEnhancedTrackingProtectionSubMenu { + clickEnhancedTrackingProtectionDefaults() + }.goBackToHomeScreen { + }.openTabDrawer { + }.openTab(defaultWebPage.title) { + clickEnhancedTrackingProtectionPanel() + verifyEnhancedTrackingProtectionSwitch() + // Turning off TP Switch results in adding the WebPage to exception list + clickEnhancedTrackingProtectionSwitchOffOn() + } + } + } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt index 070831d2c..576c6d01a 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt @@ -23,6 +23,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withResourceName import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By @@ -144,6 +145,8 @@ class BrowserRobot { fun verifyEnhancedTrackingProtectionSwitch() = assertEnhancedTrackingProtectionSwitch() + fun clickEnhancedTrackingProtectionSwitchOffOn() = onView(withResourceName("switch_widget")).click() + fun verifyProtectionSettingsButton() = assertProtectionSettingsButton() fun verifyEnhancedTrackingOptions() { @@ -188,6 +191,8 @@ class BrowserRobot { fun clickEnhancedTrackingProtectionPanel() = enhancedTrackingProtectionPanel().click() + fun verifyEnhancedTrackingProtectionPanelNotVisible() = assertEnhancedTrackingProtectionPanelNotVisible() + fun clickContextOpenLinkInNewTab() { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) mDevice.waitNotNull( @@ -420,6 +425,11 @@ private fun assertNavURLBar() = navURLBar() fun enhancedTrackingProtectionPanel() = onView(withId(R.id.mozac_browser_toolbar_tracking_protection_indicator)) +private fun assertEnhancedTrackingProtectionPanelNotVisible() { + enhancedTrackingProtectionPanel() + .check(matches(withEffectiveVisibility(Visibility.GONE))) +} + private fun assertEnhancedTrackingProtectionSwitch() { withText(R.id.trackingProtectionSwitch) .matches(withEffectiveVisibility(Visibility.VISIBLE)) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt index 18b413c3c..6d11be804 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt @@ -141,6 +141,7 @@ class SettingsRobot { } fun openEnhancedTrackingProtectionSubMenu(interact: SettingsSubMenuEnhancedTrackingProtectionRobot.() -> Unit): SettingsSubMenuEnhancedTrackingProtectionRobot.Transition { + scrollToElementByText("Enhanced Tracking Protection") fun enhancedTrackingProtectionButton() = onView(withText("Enhanced Tracking Protection")) enhancedTrackingProtectionButton().click() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuEnhancedTrackingProtectionRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuEnhancedTrackingProtectionRobot.kt index 5998f6d06..340699e58 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuEnhancedTrackingProtectionRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuEnhancedTrackingProtectionRobot.kt @@ -7,6 +7,7 @@ package org.mozilla.fenix.ui.robots import androidx.preference.R import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.Espresso.pressBack import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.matcher.ViewMatchers.hasDescendant @@ -23,9 +24,11 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import org.hamcrest.CoreMatchers.allOf +import org.hamcrest.CoreMatchers.not import org.mozilla.fenix.helpers.assertIsChecked import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.isChecked +import org.mozilla.fenix.helpers.isEnabled /** * Implementation of Robot Pattern for the settings Enhanced Tracking Protection sub menu. @@ -44,8 +47,12 @@ class SettingsSubMenuEnhancedTrackingProtectionRobot { fun verifyEnhancedTrackingProtectionOptions() = assertEnhancedTrackingProtectionOptions() + fun verifyEnhancedTrackingProtectionOptionsGrayedOut() = assertEnhancedTrackingProtectionOptionsGrayedOut() + fun verifyEnhancedTrackingProtectionDefaults() = assertEnhancedTrackingProtectionDefaults() + fun clickEnhancedTrackingProtectionDefaults() = onView(withResourceName("switch_widget")).click() + fun verifyRadioButtonDefaults() = assertRadioButtonDefaults() fun verifyEnhancedTrackingProtectionProtectionSubMenuItems() { @@ -61,6 +68,16 @@ class SettingsSubMenuEnhancedTrackingProtectionRobot { class Transition { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())!! + fun goBackToHomeScreen(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition { + // To settings + goBackButton().click() + // To HomeScreen + pressBack() + + HomeScreenRobot().interact() + return HomeScreenRobot.Transition() + } + fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition { goBackButton().click() @@ -141,6 +158,31 @@ private fun assertEnhancedTrackingProtectionOptions() { .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) } +private fun assertEnhancedTrackingProtectionOptionsGrayedOut() { + onView(withText("Standard (default)")) + .check(matches(not(isEnabled(true)))) + + val stdText = "Blocks fewer trackers. Pages will load normally." + onView(withText(stdText)) + .check(matches(not(isEnabled(true)))) + + onView(withText("Strict")) + .check(matches(not(isEnabled(true)))) + + val strictText = + "Blocks more trackers, ads, and popups. Pages load faster, but some functionality might not work." + onView(withText(strictText)) + .check(matches(not(isEnabled(true)))) + + onView(withText("Custom")) + .check(matches(not(isEnabled(true)))) + + val customText = + "Choose which trackers and scripts to block." + onView(withText(customText)) + .check(matches(not(isEnabled(true)))) +} + private fun assertEnhancedTrackingProtectionDefaults() { onView(withResourceName("switch_widget")).check( matches( From 7e7d69cb8e4514c86c4677b3b18193c5cb66f884 Mon Sep 17 00:00:00 2001 From: Johan Lorenzo Date: Tue, 21 Jul 2020 16:42:08 +0200 Subject: [PATCH 16/31] Bug 1652979 - Stop producing fennecNightly, fenixNightly and fenixBeta (#12225) --- app/build.gradle | 48 +------------------ app/src/main/java/org/mozilla/fenix/Config.kt | 14 +----- .../org/mozilla/fenix/components/Analytics.kt | 3 -- .../metrics/AdjustMetricsService.kt | 3 +- taskcluster/ci/browsertime/kind.yml | 2 +- taskcluster/ci/build/kind.yml | 46 ++---------------- taskcluster/ci/config.yml | 2 - taskcluster/ci/mark-as-shipped/kind.yml | 4 +- taskcluster/ci/push-apk/kind.yml | 4 +- taskcluster/ci/raptor/kind.yml | 2 +- taskcluster/ci/signing/kind.yml | 27 ++--------- 11 files changed, 19 insertions(+), 136 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a710bb43f..ff6ef3d6a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -72,20 +72,6 @@ android { "sharedUserId": "org.mozilla.fenix.performancetest.sharedID" ] } - fenixNightly releaseTemplate >> { - applicationIdSuffix ".fenix.nightly" - buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" - def deepLinkSchemeValue = "fenix-nightly" - buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\"" - manifestPlaceholders = ["deepLinkScheme": deepLinkSchemeValue] - } - fenixBeta releaseTemplate >> { - applicationIdSuffix ".fenix.beta" - buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" - def deepLinkSchemeValue = "fenix-beta" - buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\"" - manifestPlaceholders = ["deepLinkScheme": deepLinkSchemeValue] - } fenixProduction releaseTemplate >> { applicationIdSuffix ".fenix" buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" @@ -127,23 +113,6 @@ android { "deepLinkScheme": deepLinkSchemeValue ] } - fennecNightly releaseTemplate >> { - buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" - applicationIdSuffix ".fennec_aurora" - def deepLinkSchemeValue = "fenix-nightly" - buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\"" - manifestPlaceholders = [ - // This release type is meant to replace Firefox (Release channel) and therefore needs to inherit - // its sharedUserId for all eternity. See: - // https://searchfox.org/mozilla-central/search?q=moz_android_shared_id&case=false®exp=false&path= - // Shipping an app update without sharedUserId can have - // fatal consequences. For example see: - // - https://issuetracker.google.com/issues/36924841 - // - https://issuetracker.google.com/issues/36905922 - "sharedUserId": "org.mozilla.fennec.sharedID", - "deepLinkScheme": deepLinkSchemeValue - ] - } } variantFilter { // There's a "release" build type that exists by default that we don't use (it's replaced by "nightly" and "beta") @@ -157,19 +126,12 @@ android { // |--------------------|---------------|-----------| // | debug | ✅ | ✅ | Both variants for testing and development. // | forPerformanceTest | ✅ | ✅ | Both variants unless the perf team only cares about Nightly (TBD). - // | fenixNightly | ✅ | ✅ | Built with both, but only the "geckoNightly" one is published to Google Play - // | fenixBeta | ❌ | ✅ | Fenix Beta ships with GV Beta - // | fenixProduction | ❌ | ✅ | Fenix Production ships with GV Beta + // | fenixProduction | ✅ | ❌ | Fenix Production (to be renamed `Nightly`) ships with GV Nightly // | fennecProduction | ❌ | ✅ | Fenix build to replace production Firefox builds // | fennecBeta | ❌ | ✅ | Fenix build to replace beta Firefox builds - // | fennecNightly | ✅ | ❌ | Fenix build to replace Nightly Firefox builds def flavors = flavors*.name.toString().toLowerCase() - if (buildType.name == 'fenixBeta' && flavors.contains("geckonightly")) { - setIgnore true - } - if (buildType.name == 'fenixProduction' && flavors.contains("geckobeta")) { setIgnore true } @@ -177,10 +139,6 @@ android { if ((buildType.name == 'fennecProduction' || buildType.name == 'fennecBeta') && flavors.contains("geckonightly")) { setIgnore true } - - if (buildType.name == 'fennecNightly' && flavors.contains("geckobeta")) { - setIgnore true - } } aaptOptions { @@ -207,10 +165,6 @@ android { androidTest { resources.srcDirs += ['src/androidTest/resources'] } - fennecNightly { - java.srcDirs = ['src/migration/java'] - manifest.srcFile "src/migration/AndroidManifest.xml" - } fennecBeta { java.srcDirs = ['src/migration/java'] manifest.srcFile "src/migration/AndroidManifest.xml" diff --git a/app/src/main/java/org/mozilla/fenix/Config.kt b/app/src/main/java/org/mozilla/fenix/Config.kt index bc7f819d1..1cc1ac176 100644 --- a/app/src/main/java/org/mozilla/fenix/Config.kt +++ b/app/src/main/java/org/mozilla/fenix/Config.kt @@ -7,13 +7,10 @@ package org.mozilla.fenix enum class ReleaseChannel { FenixDebug, - FenixNightly, - FenixBeta, FenixProduction, FennecProduction, - FennecBeta, - FennecNightly; + FennecBeta; val isReleased: Boolean get() = when (this) { @@ -33,8 +30,6 @@ enum class ReleaseChannel { val isReleaseOrBeta: Boolean get() = when (this) { - FenixProduction -> true - FenixBeta -> true FennecProduction -> true FennecBeta -> true else -> false @@ -43,14 +38,11 @@ enum class ReleaseChannel { val isBeta: Boolean get() = when (this) { FennecBeta -> true - FenixBeta -> true else -> false } val isNightlyOrDebug: Boolean get() = when (this) { - FenixNightly -> true - FennecNightly -> true FenixDebug -> true FenixProduction -> true else -> false @@ -66,12 +58,9 @@ enum class ReleaseChannel { object Config { val channel = when (BuildConfig.BUILD_TYPE) { "fenixProduction" -> ReleaseChannel.FenixProduction - "fenixBeta" -> ReleaseChannel.FenixBeta - "fenixNightly" -> ReleaseChannel.FenixNightly "debug" -> ReleaseChannel.FenixDebug "fennecProduction" -> ReleaseChannel.FennecProduction "fennecBeta" -> ReleaseChannel.FennecBeta - "fennecNightly" -> ReleaseChannel.FennecNightly // Builds for local performance analysis, recording benchmarks, automation, etc. // This should be treated like a released channel because we want to test @@ -86,7 +75,6 @@ object Config { } private val fennecChannels: List = listOf( - ReleaseChannel.FennecNightly, ReleaseChannel.FennecBeta, ReleaseChannel.FennecProduction ) diff --git a/app/src/main/java/org/mozilla/fenix/components/Analytics.kt b/app/src/main/java/org/mozilla/fenix/components/Analytics.kt index 12beefc36..10ddbd2ca 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Analytics.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Analytics.kt @@ -103,11 +103,8 @@ private fun getSentryProjectUrl(): String? { val baseUrl = "https://sentry.prod.mozaws.net/operations" return when (Config.channel) { ReleaseChannel.FenixProduction -> "$baseUrl/fenix" - ReleaseChannel.FenixBeta -> "$baseUrl/fenix-beta" - ReleaseChannel.FenixNightly -> "$baseUrl/fenix-nightly" ReleaseChannel.FennecProduction -> "$baseUrl/fenix-fennec" ReleaseChannel.FennecBeta -> "$baseUrl/fenix-fennec-beta" - ReleaseChannel.FennecNightly -> "$baseUrl/fenix-fennec-nightly" else -> null } } diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/AdjustMetricsService.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/AdjustMetricsService.kt index 6c75f9785..baf4566d8 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/AdjustMetricsService.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/AdjustMetricsService.kt @@ -13,7 +13,6 @@ import com.adjust.sdk.AdjustConfig import com.adjust.sdk.LogLevel import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.Config -import org.mozilla.fenix.ReleaseChannel import org.mozilla.fenix.ext.settings class AdjustMetricsService(private val application: Application) : MetricsService { @@ -23,7 +22,7 @@ class AdjustMetricsService(private val application: Application) : MetricsServic if ((BuildConfig.ADJUST_TOKEN.isNullOrBlank())) { Log.i(LOGTAG, "No adjust token defined") - if (Config.channel.isReleased && Config.channel != ReleaseChannel.FennecNightly) { + if (Config.channel.isReleased) { throw IllegalStateException("No adjust token defined for release build") } diff --git a/taskcluster/ci/browsertime/kind.yml b/taskcluster/ci/browsertime/kind.yml index 253e74e0e..6eeab0fa8 100644 --- a/taskcluster/ci/browsertime/kind.yml +++ b/taskcluster/ci/browsertime/kind.yml @@ -82,7 +82,7 @@ job-defaults: - '--app=fenix' - '--browsertime' - '--cold' - - '--binary=org.mozilla.fenix.nightly' + - '--binary=org.mozilla.fenix' - '--activity=org.mozilla.fenix.IntentReceiverActivity' - '--download-symbols=ondemand' - '--browsertime-node=$MOZ_FETCHES_DIR/node/bin/node' diff --git a/taskcluster/ci/build/kind.yml b/taskcluster/ci/build/kind.yml index 9314d59b7..91299a9c8 100644 --- a/taskcluster/ci/build/kind.yml +++ b/taskcluster/ci/build/kind.yml @@ -93,17 +93,6 @@ jobs: treeherder: symbol: forPerformanceTest(B) - nightly: - attributes: - nightly: true - include-nightly-version: true - include-shippable-secrets: true - run: - geckoview-engine: geckoNightly - gradle-build-type: fenixNightly - treeherder: - symbol: nightly(B) - nightly-simulation: attributes: nightly: false @@ -112,47 +101,22 @@ jobs: include-shippable-secrets: true run: geckoview-engine: geckoNightly - gradle-build-type: fennecNightly + gradle-build-type: fenixProduction treeherder: symbol: nightlySim(B) - beta: - attributes: - release-type: beta - include-release-version: true - include-shippable-secrets: true - filter-incomplete-translations: true - run: - geckoview-engine: geckoBeta - gradle-build-type: fenixBeta - run-on-tasks-for: [github-release] - treeherder: - symbol: beta(B) - - # XXX `production` is now the new nightly. We keep this name around while we officially remove - # `nightly` and `fennec-nightly` - production: + nightly: attributes: nightly: true include-nightly-version: true include-shippable-secrets: true run: geckoview-engine: geckoNightly + # XXX `fenixProduction` is now the new nightly. gradle-build-type: fenixProduction - run-on-tasks-for: [github-release] + run-on-tasks-for: [] treeherder: - symbol: production(B) - - fennec-nightly: - attributes: - nightly: true - include-nightly-version: true - include-shippable-secrets: true - run: - geckoview-engine: geckoNightly - gradle-build-type: fennecNightly - treeherder: - symbol: nightlyFennec(B) + symbol: nightly(B) fennec-beta: attributes: diff --git a/taskcluster/ci/config.yml b/taskcluster/ci/config.yml index ee2db8db0..d524794fe 100644 --- a/taskcluster/ci/config.yml +++ b/taskcluster/ci/config.yml @@ -2,7 +2,6 @@ trust-domain: mobile treeherder: group-names: - 'beta': 'Beta-related tasks' 'betaFennec': 'Beta-related tasks with same APK configuration as Fennec' 'Btime': 'Raptor-Browsertime tests' 'bump': 'Bump dependencies' @@ -12,7 +11,6 @@ treeherder: 'I': 'Docker Image Builds' 'nightly': 'Nightly-related tasks' 'nightlySim': 'Nightly-related tasks that run on each github push' - 'nightlyFennec': 'Nightly-related tasks with same APK configuration as Fennec' 'production': 'Release-related tasks' 'productionFennec': 'Production-related tasks with same APK configuration as Fennec' 'Rap': 'Raptor tests' diff --git a/taskcluster/ci/mark-as-shipped/kind.yml b/taskcluster/ci/mark-as-shipped/kind.yml index af8d210f4..b7cc1772c 100644 --- a/taskcluster/ci/mark-as-shipped/kind.yml +++ b/taskcluster/ci/mark-as-shipped/kind.yml @@ -17,8 +17,8 @@ primary-dependency: push-apk group-by: build-type only-for-build-types: - - fenix-beta - - fenix-production + - fennec-beta + - fennec-production job-template: description: Mark Fenix as shipped in ship-it diff --git a/taskcluster/ci/push-apk/kind.yml b/taskcluster/ci/push-apk/kind.yml index f1841f16b..4135e4ac6 100644 --- a/taskcluster/ci/push-apk/kind.yml +++ b/taskcluster/ci/push-apk/kind.yml @@ -19,7 +19,7 @@ group-by: build-type only-for-build-types: - fennec-beta - fennec-production - - production + - nightly job-template: description: Publish Fenix @@ -30,7 +30,7 @@ job-template: by-build-type: fennec-beta: fennec-beta fennec-production: fennec-production - production: production + nightly: production dep: by-level: '3': false diff --git a/taskcluster/ci/raptor/kind.yml b/taskcluster/ci/raptor/kind.yml index 135a05b5c..961bff746 100644 --- a/taskcluster/ci/raptor/kind.yml +++ b/taskcluster/ci/raptor/kind.yml @@ -77,7 +77,7 @@ job-defaults: - './test-linux.sh' - '--cfg=mozharness/configs/raptor/android_hw_config.py' - '--app=fenix' - - '--binary=org.mozilla.fenix.nightly' + - '--binary=org.mozilla.fenix' - '--activity=org.mozilla.fenix.IntentReceiverActivity' - '--download-symbols=ondemand' - '--no-conditioned-profile' diff --git a/taskcluster/ci/signing/kind.yml b/taskcluster/ci/signing/kind.yml index 45f38ff11..1436c1efb 100644 --- a/taskcluster/ci/signing/kind.yml +++ b/taskcluster/ci/signing/kind.yml @@ -20,7 +20,7 @@ job-template: description: Sign Fenix worker-type: by-build-type: - (fennec-.+|nightly|beta|production|android-test-nightly): + (fennec-.+|nightly|android-test-nightly): by-level: '3': signing default: dep-signing @@ -32,37 +32,20 @@ job-template: by-level: '3': fennec-production-signing default: dep-signing - fennec-nightly: - by-level: - '3': fennec-nightly-signing - default: dep-signing - nightly: - by-level: - '3': nightly-signing - default: dep-signing android-test-nightly: by-level: - '3': nightly-signing + '3': production-signing default: dep-signing performance-test: dep-signing - beta: - by-level: - '3': beta-signing - default: dep-signing - production: + nightly: by-level: '3': production-signing default: dep-signing default: dep-signing - signing-format: - by-build-type: - # Fennec nightly cannot have the sha256 checksums. For more details, see - # https://github.com/mozilla-releng/scriptworker-scripts/pull/102#issue-349016967 - fennec-nightly: autograph_apk_fennec_sha1 - default: autograph_apk + signing-format: autograph_apk index: by-build-type: - (fennec-.+|nightly|performance-test|beta|production|debug|nightly-simulation): + (fennec-.+|performance-test|nightly|debug|nightly-simulation): type: signing default: {} run-on-tasks-for: From 9a7154e95ebd848b3f8f8d9ce579cbc75ebb0d52 Mon Sep 17 00:00:00 2001 From: mcarare Date: Wed, 1 Jul 2020 09:45:10 +0300 Subject: [PATCH 17/31] For android-l10n #241: Mark a11y link type as not translatable. --- app/src/main/res/values/static_strings.xml | 3 +++ app/src/main/res/values/strings.xml | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/static_strings.xml b/app/src/main/res/values/static_strings.xml index fe7ea6b8f..0d23cbf7c 100644 --- a/app/src/main/res/values/static_strings.xml +++ b/app/src/main/res/values/static_strings.xml @@ -33,4 +33,7 @@ Secret Settings Use New Tab Tray + + + link diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 505fe2935..33d219f38 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1432,6 +1432,4 @@ To add a new top site, remove one. Long press the site and select remove. OK, Got It - - link From a13c6e8d332089b499512fb6e9a3ad102848539d Mon Sep 17 00:00:00 2001 From: MickeyMoz Date: Tue, 21 Jul 2020 14:05:48 +0000 Subject: [PATCH 18/31] Update Android Components version to 51.0.20200721130108. --- 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 2a48f3de7..71516a6cd 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 = "51.0.20200720130437" + const val VERSION = "51.0.20200721130108" } From 3a85251595d9624da7280e6eb30bfd07154ecf28 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Tue, 21 Jul 2020 15:21:41 +1000 Subject: [PATCH 19/31] Cleanup locall publish flows for application-services --- settings.gradle | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/settings.gradle b/settings.gradle index 493215ab8..8440fc306 100644 --- a/settings.gradle +++ b/settings.gradle @@ -45,22 +45,14 @@ if (localProperties != null) { if (appServicesLocalPath != null) { log("Enabling automatic publication of application-services from: $appServicesLocalPath") - def publishAppServicesCmd = ["./automation/publish_to_maven_local_if_modified.py"] - // Application-services doesn't build on native Windows. However, it still makes sense to - // enable these workflows on Windows, even if it isn't quote as automatic as elsewhere - - // specifically, you must run the build command on WSL, but after that you can happily build - // and debug directly from within Android Studio on native Windows - but only after following - // https://github.com/mozilla/application-services/blob/master/docs/howtos/setup-android-build-environment.md#using-windows - // So rather than fail we make noise... + // Windows can't execute .py files directly, so we assume a "manually installed" python, + // which comes with a "py" launcher and respects the shebang line to specify the version. + def publishAppServicesCmd = []; if (System.properties['os.name'].toLowerCase().contains('windows')) { - log('NOTE: The autoPublish workflows do not work on native windows.'); - log('You must manually ensure that the following command has completed successfully in WSL:'); - log("> $publishAppServicesCmd"); - log("(from the '$appServicesLocalPath' directory)"); - log('Then restart the build'); - } else { - runCmd(publishAppServicesCmd, appServicesLocalPath, "Published application-services for local development.", false) + publishAppServicesCmd << "py"; } + publishAppServicesCmd << "./automation/publish_to_maven_local_if_modified.py"; + runCmd(publishAppServicesCmd, appServicesLocalPath, "Published application-services for local development.", false) } else { log("Disabled auto-publication of application-services. Enable it by settings '$settingAppServicesPath' in local.properties") } @@ -68,10 +60,8 @@ if (localProperties != null) { String androidComponentsLocalPath = localProperties.getProperty(settingAndroidComponentsPath) if (androidComponentsLocalPath != null) { - // android-components does build on native windows, so it doesn't get the special Windows treatment above. - // But it doesn't like executing .py files directly. We assume a "manually installed" python, - // which comes with a "py" launcher and respects the shebang line to specify the version. log("Enabling automatic publication of android-components from: $androidComponentsLocalPath") + // As above, hacks to execute .py files on Windows. def publishAcCmd = []; if (System.properties['os.name'].toLowerCase().contains('windows')) { publishAcCmd << "py"; From 0abb2c4f8b57985d6b8d89c7db6610cb7a6ff874 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Fri, 17 Jul 2020 16:19:32 -0700 Subject: [PATCH 20/31] Add tests for web push integration --- .../fenix/push/WebPushEngineIntegration.kt | 9 +- .../push/WebPushEngineIntegrationTest.kt | 183 ++++++++++++++++++ 2 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 app/src/test/java/org/mozilla/fenix/push/WebPushEngineIntegrationTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/push/WebPushEngineIntegration.kt b/app/src/main/java/org/mozilla/fenix/push/WebPushEngineIntegration.kt index c23bd13df..646b20ee9 100644 --- a/app/src/main/java/org/mozilla/fenix/push/WebPushEngineIntegration.kt +++ b/app/src/main/java/org/mozilla/fenix/push/WebPushEngineIntegration.kt @@ -6,7 +6,7 @@ package org.mozilla.fenix.push import android.util.Base64 import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import mozilla.components.concept.engine.Engine import mozilla.components.concept.engine.webpush.WebPushDelegate @@ -22,7 +22,8 @@ import mozilla.components.support.base.log.logger.Logger */ class WebPushEngineIntegration( private val engine: Engine, - private val pushFeature: AutoPushFeature + private val pushFeature: AutoPushFeature, + private val coroutineScope: CoroutineScope = MainScope() ) : AutoPushFeature.Observer { private var handler: WebPushHandler? = null @@ -39,13 +40,13 @@ class WebPushEngineIntegration( } override fun onMessageReceived(scope: PushScope, message: ByteArray?) { - CoroutineScope(Dispatchers.Main).launch { + coroutineScope.launch { handler?.onPushMessage(scope, message) } } override fun onSubscriptionChanged(scope: PushScope) { - CoroutineScope(Dispatchers.Main).launch { + coroutineScope.launch { handler?.onSubscriptionChanged(scope) } } diff --git a/app/src/test/java/org/mozilla/fenix/push/WebPushEngineIntegrationTest.kt b/app/src/test/java/org/mozilla/fenix/push/WebPushEngineIntegrationTest.kt new file mode 100644 index 000000000..08354def9 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/push/WebPushEngineIntegrationTest.kt @@ -0,0 +1,183 @@ +/* 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.push + +import android.util.Base64 +import io.mockk.Called +import io.mockk.CapturingSlot +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.slot +import io.mockk.unmockkStatic +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.test.runBlockingTest +import mozilla.components.concept.engine.Engine +import mozilla.components.concept.engine.webpush.WebPushDelegate +import mozilla.components.concept.engine.webpush.WebPushHandler +import mozilla.components.concept.engine.webpush.WebPushSubscription +import mozilla.components.feature.push.AutoPushFeature +import mozilla.components.feature.push.AutoPushSubscription +import org.junit.After +import org.junit.Before +import org.junit.Test + +@ExperimentalCoroutinesApi +class WebPushEngineIntegrationTest { + + private val scope = TestCoroutineScope() + @MockK private lateinit var engine: Engine + @MockK private lateinit var pushFeature: AutoPushFeature + @MockK(relaxed = true) private lateinit var handler: WebPushHandler + private lateinit var delegate: CapturingSlot + private lateinit var integration: WebPushEngineIntegration + + @Before + fun setup() { + MockKAnnotations.init(this) + mockkStatic(Base64::class) + delegate = slot() + + every { engine.registerWebPushDelegate(capture(delegate)) } returns handler + every { pushFeature.register(any()) } just Runs + every { pushFeature.unregister(any()) } just Runs + every { Base64.decode(any(), any()) } answers { firstArg() } + + integration = WebPushEngineIntegration(engine, pushFeature, scope) + } + + @After + fun teardown() { + unmockkStatic(Base64::class) + } + + @Test + fun `methods are no-op before calling start`() = scope.runBlockingTest { + integration.onMessageReceived("push", null) + integration.onSubscriptionChanged("push") + verify { handler wasNot Called } + + integration.start() + + integration.onMessageReceived("push", null) + verify { handler.onPushMessage("push", null) } + + integration.onSubscriptionChanged("push") + verify { handler.onSubscriptionChanged("push") } + } + + @Test + fun `start and stop register and unregister pushFeature`() { + integration.start() + verify { pushFeature.register(integration) } + + integration.stop() + verify { pushFeature.unregister(integration) } + } + + @Test + fun `delegate calls getSubscription`() { + integration.start() + val slot = slot<(AutoPushSubscription?) -> Unit>() + every { pushFeature.getSubscription("scope", block = capture(slot)) } just Runs + + val onSubscription = mockk<(WebPushSubscription?) -> Unit>(relaxed = true) + delegate.captured.onGetSubscription("scope", onSubscription) + + verify { onSubscription wasNot Called } + slot.captured(AutoPushSubscription( + scope = "scope", + publicKey = "abc", + endpoint = "def", + authKey = "xyz", + appServerKey = null + )) + + verify { + onSubscription( + WebPushSubscription( + scope = "scope", + publicKey = "abc".toByteArray(), + endpoint = "def", + authSecret = "xyz".toByteArray(), + appServerKey = null + ) + ) + } + } + + @Test + fun `delegate calls subscribe`() { + integration.start() + val onSubscribeError = slot<() -> Unit>() + val onSubscribe = slot<(AutoPushSubscription?) -> Unit>() + every { + pushFeature.subscribe( + scope = "scope", + appServerKey = null, + onSubscribeError = capture(onSubscribeError), + onSubscribe = capture(onSubscribe) + ) + } just Runs + + val onSubscription = mockk<(WebPushSubscription?) -> Unit>(relaxed = true) + delegate.captured.onSubscribe("scope", null, onSubscription) + + verify { onSubscription wasNot Called } + + onSubscribeError.captured() + verify { onSubscription(null) } + + onSubscribe.captured(AutoPushSubscription( + scope = "scope", + publicKey = "abc", + endpoint = "def", + authKey = "xyz", + appServerKey = null + )) + verify { + onSubscription( + WebPushSubscription( + scope = "scope", + publicKey = "abc".toByteArray(), + endpoint = "def", + authSecret = "xyz".toByteArray(), + appServerKey = null + ) + ) + } + } + + @Test + fun `delegate calls unsubscribe`() { + integration.start() + val onUnsubscribeError = slot<() -> Unit>() + val onUnsubscribe = slot<(Boolean) -> Unit>() + every { + pushFeature.unsubscribe( + scope = "scope", + onUnsubscribeError = capture(onUnsubscribeError), + onUnsubscribe = capture(onUnsubscribe) + ) + } just Runs + + val onUnsubscription = mockk<(Boolean) -> Unit>(relaxed = true) + delegate.captured.onUnsubscribe("scope", onUnsubscription) + + verify { onUnsubscription wasNot Called } + + onUnsubscribeError.captured() + verify { onUnsubscription(false) } + + onUnsubscribe.captured(true) + verify { onUnsubscription(true) } + } +} From 0e1e9645b05b45a232fb432a728cb3da6a4648d2 Mon Sep 17 00:00:00 2001 From: gmierz Date: Tue, 21 Jul 2020 11:34:48 -0400 Subject: [PATCH 21/31] Update visual-metric code. --- .../visual-metrics/run-visual-metrics.py | 69 +++-- .../docker/visual-metrics/similarity.py | 291 ++++++++++++------ 2 files changed, 237 insertions(+), 123 deletions(-) diff --git a/taskcluster/docker/visual-metrics/run-visual-metrics.py b/taskcluster/docker/visual-metrics/run-visual-metrics.py index 4ae05172d..14b15221f 100644 --- a/taskcluster/docker/visual-metrics/run-visual-metrics.py +++ b/taskcluster/docker/visual-metrics/run-visual-metrics.py @@ -27,12 +27,16 @@ from voluptuous import ALLOW_EXTRA, Required, Schema #: The directory where artifacts from this job will be placed. OUTPUT_DIR = Path("/", "builds", "worker", "artifacts") + #: A job to process through visualmetrics.py @attr.s class Job: #: The name of the test. test_name = attr.ib(type=str) + #: The extra options for this job. + extra_options = attr.ib(type=str) + #: json_path: The path to the ``browsertime.json`` file on disk. json_path = attr.ib(type=Path) @@ -44,7 +48,11 @@ class Job: JOB_SCHEMA = Schema( { Required("jobs"): [ - {Required("test_name"): str, Required("browsertime_json_path"): str} + { + Required("test_name"): str, + Required("browsertime_json_path"): str, + Required("extra_options"): [str], + } ], Required("application"): {Required("name"): str, "version": str}, Required("extra_options"): [str], @@ -80,7 +88,7 @@ def run_command(log, cmd): return e.returncode, e.output -def append_result(log, suites, test_name, name, result): +def append_result(log, suites, test_name, name, result, extra_options): """Appends a ``name`` metrics result in the ``test_name`` suite. Args: @@ -98,10 +106,16 @@ def append_result(log, suites, test_name, name, result): log.error("Could not convert value", name=name) log.error("%s" % result) result = 0 - if test_name not in suites: - suites[test_name] = {"name": test_name, "subtests": {}} - subtests = suites[test_name]["subtests"] + if test_name in suites and suites[test_name]["extraOptions"] != extra_options: + missing = set(extra_options) - set(suites[test_name]["extraOptions"]) + test_name = test_name + "-".join(list(missing)) + + subtests = suites.setdefault( + test_name, + {"name": test_name, "subtests": {}, "extraOptions": extra_options} + )["subtests"] + if name not in subtests: subtests[name] = { "name": name, @@ -241,6 +255,8 @@ def main(log, args): jobs.append( Job( test_name=job["test_name"], + extra_options=len(job["extra_options"]) > 0 and + job["extra_options"] or jobs_json["extra_options"], json_path=browsertime_json_path, video_path=browsertime_json_path.parent / video, ) @@ -273,45 +289,34 @@ def main(log, args): # Python 3.5 requires a str object (not 3.6+) res = json.loads(res.decode("utf8")) for name, value in res.items(): - append_result(log, suites, job.test_name, name, value) + append_result(log, suites, job.test_name, name, value, job.extra_options) suites = [get_suite(suite) for suite in suites.values()] perf_data = { "framework": {"name": "browsertime"}, "application": jobs_json["application"], - "type": "vismet", + "type": "pageload", "suites": suites, } - for entry in suites: - entry["extraOptions"] = jobs_json["extra_options"] # Try to get the similarity for all possible tests, this means that we # will also get a comparison of recorded vs. live sites to check # the on-going quality of our recordings. - similarity = None - if "android" in os.getenv("TC_PLATFORM", ""): - try: - from similarity import calculate_similarity - similarity = calculate_similarity(jobs_json, fetch_dir, OUTPUT_DIR, log) - except Exception: - log.info("Failed to calculate similarity score", exc_info=True) - - if similarity: - suites[0]["subtests"].append({ - "name": "Similarity3D", - "value": similarity[0], - "replicates": [similarity[0]], - "lowerIsBetter": False, - "unit": "a.u.", - }) - suites[0]["subtests"].append({ - "name": "Similarity2D", - "value": similarity[1], - "replicates": [similarity[1]], - "lowerIsBetter": False, - "unit": "a.u.", - }) + try: + from similarity import calculate_similarity + for name, value in calculate_similarity(jobs_json, fetch_dir, OUTPUT_DIR).items(): + if value is None: + continue + suites[0]["subtests"].append({ + "name": name, + "value": value, + "replicates": [value], + "lowerIsBetter": False, + "unit": "a.u.", + }) + except Exception: + log.info("Failed to calculate similarity score", exc_info=True) # Validates the perf data complies with perfherder schema. # The perfherder schema uses jsonschema so we can't use voluptuous here. diff --git a/taskcluster/docker/visual-metrics/similarity.py b/taskcluster/docker/visual-metrics/similarity.py index 5820e531e..f56e15875 100644 --- a/taskcluster/docker/visual-metrics/similarity.py +++ b/taskcluster/docker/visual-metrics/similarity.py @@ -10,6 +10,7 @@ import os import pathlib import shutil import socket +import structlog import tarfile import tempfile import urllib @@ -19,8 +20,24 @@ from matplotlib import pyplot as plt from scipy.stats import spearmanr -def open_data(file): - return cv2.VideoCapture(str(file)) +log = None + + +# We add the `and` conditions to it later +base_ad_query = { + "from": "task", + "limit": 1000, + "where": { + "and": [] + }, + "select": [ + "action.start_time", + "run.name", + "task.artifacts", + "task.group.id", + "task.id" + ], +} def socket_timeout(value=120): @@ -38,8 +55,12 @@ def socket_timeout(value=120): return _socket_timeout +def _open_data(file): + return cv2.VideoCapture(str(file)) + + @socket_timeout(120) -def query_activedata(query_json, log): +def _query_activedata(query_json): """Used to run queries on active data.""" active_data_url = "http://activedata.allizom.org/query" @@ -59,7 +80,7 @@ def query_activedata(query_json, log): @socket_timeout(120) -def download(url, loc, log): +def _download(url, loc): """Downloads from a url (with a timeout).""" log.info("Downloading %s" % url) try: @@ -70,7 +91,7 @@ def download(url, loc, log): return True -def get_frames(video): +def _get_frames(video): """Gets all frames from a video into a list.""" allframes = [] while video.isOpened(): @@ -84,77 +105,11 @@ def get_frames(video): return allframes -def calculate_similarity(jobs_json, fetch_dir, output, log): - """Calculates the similarity score against the last live site test. - - The technique works as follows: - 1. Get the last live site test. - 2. For each 15x15 video pairings, build a cross-correlation matrix: - 1. Get each of the videos and calculate their histograms - across the full videos. - 2. Calculate the correlation coefficient between these two. - 3. Average the cross-correlation matrix to obtain the score. - - The 2D similarity score is the same, except that it builds a histogram - from the final frame instead of the full video. - - For finding the last live site, we use active-data. We search for - PGO android builds since this metric is only available for live sites that - run on android in mozilla-cental. Given that live sites currently - run on cron 3 days a week, then it's also reasonable to look for tasks - which have occurred before today and within the last two weeks at most. - But this is a TODO for future work, since we need to determine a better - way of selecting the last task (HG push logs?) - there's a lot that factors - into these choices, so it might require a multi-faceted approach. - - Args: - jobs_json: The jobs JSON that holds extra information. - fetch_dir: The fetch directory that holds the new videos. - log: The logger. - Returns: - Two similarity scores (3D, 2D) as a float, or None if there was an issue. - """ - app = jobs_json["application"]["name"] - test = jobs_json["jobs"][0]["test_name"] - splittest = test.split("-cold") - - cold = "" - if len(splittest) > 0: - cold = ".*cold" - test = splittest[0] - - # PGO vs. OPT shouldn't matter much, but we restrict it to PGO builds here - # for android, and desktop tests have the opt/pgo restriction removed - plat = os.getenv("TC_PLATFORM", "") - if "android" in plat: - plat = plat.replace("/opt", "/pgo") - else: - plat = plat.replace("/opt", "").replace("/pgo", "") - ad_query = { - "from": "task", - "limit": 1000, - "where": { - "and": [ - { - "regexp": { - "run.name": ".*%s.*browsertime.*-live.*%s%s.*%s.*" - % (plat, app, cold, test) - } - }, - {"not": {"prefix": {"run.name": "test-vismet"}}}, - {"in": {"repo.branch.name": ["mozilla-central"]}}, - {"gte": {"action.start_time": {"date": "today-week-week"}}}, - {"lt": {"action.start_time": {"date": "today-1day"}}}, - {"in": {"task.run.state": ["completed"]}}, - ] - }, - "select": ["action.start_time", "run.name", "task.artifacts"], - } - - # Run the AD query and find the browsertime videos to download +def _get_browsertime_results(query): + """Used to run an AD query and extract the browsertime results if they exist.""" failed = False try: - data = query_activedata(ad_query, log) + data = _query_activedata(query) except Exception as e: log.info(str(e)) failed = True @@ -162,6 +117,7 @@ def calculate_similarity(jobs_json, fetch_dir, output, log): log.info("Couldn't get activedata data") return None + # Find the newest browsertime task log.info("Found %s datums" % str(len(data["action.start_time"]))) maxind = np.argmax([float(t) for t in data["action.start_time"]]) artifacts = data["task.artifacts"][maxind] @@ -171,13 +127,20 @@ def calculate_similarity(jobs_json, fetch_dir, output, log): btime_artifact = art["url"] break if not btime_artifact: - log.info("Can't find an older live site") + log.info("Can't find an older site test") return None + log.info("Comparing videos to TASK_GROUP=%s, TASK_ID=%s" % ( + data["task.group.id"][maxind], data["task.id"][maxind] + )) + # Download the browsertime videos and untar them tmpdir = tempfile.mkdtemp() loc = os.path.join(tmpdir, "tmpfile.tgz") - if not download(btime_artifact, loc, log): + if not _download(btime_artifact, loc): + log.info( + "Failed to download browsertime-results artifact from %s" % btime_artifact + ) return None tmploc = tempfile.mkdtemp() try: @@ -191,22 +154,90 @@ def calculate_similarity(jobs_json, fetch_dir, output, log): ) return None - # Find all the videos - oldmp4s = [str(f) for f in pathlib.Path(tmploc).rglob("*.mp4")] - log.info("Found %s old videos" % str(len(oldmp4s))) - newmp4s = [str(f) for f in pathlib.Path(fetch_dir).rglob("*.mp4")] - log.info("Found %s new videos" % str(len(newmp4s))) + return tmploc - # Finally, calculate the 2D/3D score + +def _data_from_last_task(label): + """Gets the data from the last PGO/OPT task with the same label. + + We look for both OPT and PGO tasks. The difference + between them should be minimal. This method also provides + a way to compare recordings from this task to another + known task based on the TC_GROUP_ID environment varible. + """ + label_opt = label.replace("/pgo", "/opt") + label_pgo = label.replace("/opt", "/pgo") + + base_ad_query["where"]["and"] = [ + {"in": {"task.run.state": ["completed"]}}, + {"or": [ + {"eq": {"run.name": label_pgo}}, + {"eq": {"run.name": label_opt}} + ]} + ] + + task_group_id = os.getenv("TC_GROUP_ID", "") + if task_group_id: + base_ad_query["where"]["and"].append( + {"eq": {"task.group.id": task_group_id}} + ) + else: + base_ad_query["where"]["and"].extend([ + {"in": {"repo.branch.name": ["mozilla-central"]}}, + {"gte": {"action.start_time": {"date": "today-week-week"}}}, + ]) + + return _get_browsertime_results(base_ad_query) + + +def _data_from_last_live_task(label): + """Gets the data from the last live site PGO task.""" + label_live = label.replace("/opt", "/pgo").replace("tp6m", "tp6m-live") + + base_ad_query["where"]["and"] = [ + {"in": {"repo.branch.name": ["mozilla-central"]}}, + {"gte": {"action.start_time": {"date": "today-week-week"}}}, + {"in": {"task.run.state": ["completed"]}}, + {"eq": {"run.name": label_live}}, + ] + + return _get_browsertime_results(base_ad_query) + + +def _get_similarity(old_videos_info, new_videos_info, output, prefix=""): + """Calculates a similarity score for two groupings of videos. + + The technique works as follows: + 1. Get the last live site test. + 2. For each 15x15 video pairings, build a cross-correlation matrix: + 1. Get each of the videos and calculate their histograms + across the full videos. + 2. Calculate the correlation coefficient between these two. + 3. Average the cross-correlation matrix to obtain the score. + + The 2D similarity score is the same, except that it builds a histogram + from the final frame instead of the full video. + + Args: + old_videos: List of old videos. + new_videos: List of new videos (from this task). + output: Location to output videos with low similarity scores. + prefix: Prefix a string to the output. + Returns: + Two similarity scores (3D, 2D) as a float. + """ nhists = [] nhists2d = [] - total_vids = min(len(oldmp4s), len(newmp4s)) + old_videos = [entry["data"] for entry in old_videos_info] + new_videos = [entry["data"] for entry in new_videos_info] + + total_vids = min(len(old_videos), len(new_videos)) xcorr = np.zeros((total_vids, total_vids)) xcorr2d = np.zeros((total_vids, total_vids)) for i in range(total_vids): - datao = np.asarray(get_frames(open_data(oldmp4s[i]))) + datao = np.asarray(_get_frames(old_videos[i])) histo, _, _ = plt.hist(datao.flatten(), bins=255) histo2d, _, _ = plt.hist(datao[-1, :, :].flatten(), bins=255) @@ -214,7 +245,7 @@ def calculate_similarity(jobs_json, fetch_dir, output, log): for j in range(total_vids): if i == 0: # Only calculate the histograms once; it takes time - datan = np.asarray(get_frames(open_data(newmp4s[j]))) + datan = np.asarray(_get_frames(new_videos[j])) histn, _, _ = plt.hist(datan.flatten(), bins=255) histn2d, _, _ = plt.hist(datan[-1, :, :].flatten(), bins=255) @@ -237,15 +268,93 @@ def calculate_similarity(jobs_json, fetch_dir, output, log): log.info("Average 3D similarity: %s" % str(np.round(similarity, 5))) log.info("Average 2D similarity: %s" % str(np.round(similarity2d, 5))) - if similarity < 0.5: - # For really low correlations, output the worst video pairing + if np.round(similarity, 1) <= 0.7 or np.round(similarity2d, 1) <= 0.7: + # For low correlations, output the worst video pairing # so that we can visually see what the issue was minind = np.unravel_index(np.argmin(xcorr, axis=None), xcorr.shape) - oldvid = oldmp4s[minind[0]] - shutil.copyfile(oldvid, str(pathlib.Path(output, "old_video.mp4"))) + oldvid = old_videos_info[minind[0]]["path"] + shutil.copyfile(oldvid, str(pathlib.Path(output, "%sold_video.mp4" % prefix))) - newvid = newmp4s[minind[1]] - shutil.copyfile(newvid, str(pathlib.Path(output, "new_video.mp4"))) + newvid = new_videos_info[minind[1]]["path"] + shutil.copyfile(newvid, str(pathlib.Path(output, "%snew_video.mp4" % prefix))) return np.round(similarity, 5), np.round(similarity2d, 5) + + +def calculate_similarity(jobs_json, fetch_dir, output): + """Calculates the similarity score for this task. + + Here we use activedata to find the last live site that ran and + to find the last task (with the same label) that ran. Those two + tasks are then compared to the current one and 4 metrics are produced. + + For live sites, we only calculate 2 of these metrics, since the + playback similarity is not applicable to it. + + Args: + jobs_json: The jobs JSON that holds extra information. + fetch_dir: The fetch directory that holds the new videos. + output: The output directory. + Returns: + A dictionary containing up to 4 different metrics (their values default + to None if a metric couldn't be calculated): + PlaybackSimilarity: Similarity of the full playback to a live site test. + PlaybackSimilarity2D: - // - (but for the final frame only) + Similarity: Similarity of the tests video recording to its last run. + Similarity2D: - // - (but for the final frame only) + """ + global log + log = structlog.get_logger() + + label = os.getenv("TC_LABEL", "") + if not label: + log.info("TC_LABEL is undefined, cannot calculate similarity metrics") + return {} + + # Get all the newest videos from this task + new_btime_videos = [ + {"data": _open_data(str(f)), "path": str(f)} + for f in pathlib.Path(fetch_dir).rglob("*.mp4") + ] + log.info("Found %s new videos" % str(len(new_btime_videos))) + + # Get the similarity against the last task + old_btime_res = _data_from_last_task(label) + old_sim = old_sim2d = None + if old_btime_res: + old_btime_videos = [ + {"data": _open_data(str(f)), "path": str(f)} + for f in pathlib.Path(old_btime_res).rglob("*.mp4") + ] + log.info("Found %s old videos" % str(len(old_btime_videos))) + + old_sim, old_sim2d = _get_similarity( + old_btime_videos, new_btime_videos, output + ) + else: + log.info("Failed to find an older test task") + + # Compare recordings to their live site variant if it exists + live_sim = live_sim2d = None + if "live" not in jobs_json["extra_options"]: + live_btime_res = _data_from_last_live_task(label) + if live_btime_res: + live_btime_videos = [ + {"data": _open_data(str(f)), "path": str(f)} + for f in pathlib.Path(live_btime_res).rglob("*.mp4") + ] + log.info("Found %s live videos" % str(len(live_btime_videos))) + + live_sim, live_sim2d = _get_similarity( + live_btime_videos, new_btime_videos, output, prefix="live_" + ) + else: + log.info("Failed to find a live site variant") + + return { + "PlaybackSimilarity": live_sim, + "PlaybackSimilarity2D": live_sim2d, + "Similarity": old_sim, + "Similarity2D": old_sim2d, + } From 0acfa992f214dbb0994682aa24296a5b709abe27 Mon Sep 17 00:00:00 2001 From: gmierz Date: Tue, 21 Jul 2020 11:39:55 -0400 Subject: [PATCH 22/31] Test vismet. --- taskcluster/ci/browsertime/kind.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/taskcluster/ci/browsertime/kind.yml b/taskcluster/ci/browsertime/kind.yml index 6eeab0fa8..d13925c78 100644 --- a/taskcluster/ci/browsertime/kind.yml +++ b/taskcluster/ci/browsertime/kind.yml @@ -100,6 +100,7 @@ job-defaults: jobs: tp6m-1-cold: test-name: amazon + run-on-tasks-for: [github-pull-request] treeherder: symbol: 'Btime(tp6m-1-c)' From b898965426ec176cca2258f8d9503c9366c9b89a Mon Sep 17 00:00:00 2001 From: gmierz Date: Tue, 21 Jul 2020 11:40:26 -0400 Subject: [PATCH 23/31] Undo test changes. --- taskcluster/ci/browsertime/kind.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/taskcluster/ci/browsertime/kind.yml b/taskcluster/ci/browsertime/kind.yml index d13925c78..6eeab0fa8 100644 --- a/taskcluster/ci/browsertime/kind.yml +++ b/taskcluster/ci/browsertime/kind.yml @@ -100,7 +100,6 @@ job-defaults: jobs: tp6m-1-cold: test-name: amazon - run-on-tasks-for: [github-pull-request] treeherder: symbol: 'Btime(tp6m-1-c)' From 6b8eca0700026abde174bfbdbb57e2c910467e67 Mon Sep 17 00:00:00 2001 From: Kainalu Hagiwara Date: Tue, 21 Jul 2020 09:10:22 -0700 Subject: [PATCH 24/31] Move release locales to a separate config file. We should leave l10n.toml untouched unless the l10n team wants to change it. --- automation/taskcluster/l10n/locales.py | 14 ++-- l10n-release.toml | 91 +++++++++++++++++++++++++ l10n.toml | 92 -------------------------- 3 files changed, 98 insertions(+), 99 deletions(-) create mode 100644 l10n-release.toml diff --git a/automation/taskcluster/l10n/locales.py b/automation/taskcluster/l10n/locales.py index 80c87bc2c..868c71372 100644 --- a/automation/taskcluster/l10n/locales.py +++ b/automation/taskcluster/l10n/locales.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, print_function, unicode_literals import re -OPEN_LOCALES = "release_locales = [" +OPEN_LOCALES = "locales = [" CLOSE_LOCALES = "]" def trim_to_locale(str): @@ -18,11 +18,11 @@ def trim_to_locale(str): return match.group(1) -# This file is a dumb parser that converts values from '/l10n.toml' to be easily consumed from -# Python. +# This file is a dumb parser that converts values from '/l10n-release.toml' to be easily +# consumed from Python. # -# 'l10n.toml' has a very simple structure, and it is reasonable to believe that this (very basic) -# algorithm will continue to work as it is changed. +# 'l10n-release.toml' has a very simple structure, and it is reasonable to believe that this +# (very basic) algorithm will continue to work as it is changed. # # Alternatives to custom parsing that were considered: # - Using standard library module --- none exists to parse TOML @@ -31,7 +31,7 @@ def trim_to_locale(str): # - Vendoring a TOML module --- large amount of code given the use case. Introduces a security # risk def get_release_locales(): - with open(r"l10n.toml") as f: + with open(r"l10n-release.toml") as f: file = f.read().splitlines() locales_opened = False @@ -42,7 +42,7 @@ def get_release_locales(): for line in file: if line == OPEN_LOCALES: locales_opened = True - elif line == CLOSE_LOCALES and locales_opened == True: + elif line == CLOSE_LOCALES: locales_closed = True break elif locales_opened: diff --git a/l10n-release.toml b/l10n-release.toml new file mode 100644 index 000000000..789c8f550 --- /dev/null +++ b/l10n-release.toml @@ -0,0 +1,91 @@ +# Locales that should be present in release builds +# Locales that are at 70% or higher completion on https://pontoon.mozilla.org/projects/android-l10n/ +# should be in this list +locales = [ + "an", + "ar", + "ast", + "az", + "be", + "bn", + "br", + "bs", + "ca", + "cak", + "co", + "cs", + "cy", + "da", + "de", + "dsb", + "el", + "en-CA", + "en-GB", + "eo", + "es", + "es-AR", + "es-CL", + "es-ES", + "es-MX", + "et", + "eu", + "fa", + "ff", + "fi", + "fr", + "fy-NL", + "ga-IE", + "gd", + "gn", + "gu-IN", + "he", + "hi-IN", + "hr", + "hsb", + "hu", + "hy-AM", + "id", + "is", + "it", + "ja", + "ka", + "kab", + "kk", + "kn", + "ko", + "lij", + "lo", + "lt", + "ml", + "mr", + "my", + "nb-NO", + "nl", + "nn-NO", + "oc", + "pa-IN", + "pl", + "pt-BR", + "pt-PT", + "rm", + "ro", + "ru", + "sk", + "sl", + "sq", + "sr", + "su", + "sv-SE", + "ta", + "te", + "th", + "tr", + "trs", + "uk", + "ur", + "vec", + "vi", + "zh-CN", + "zh-TW", +] + diff --git a/l10n.toml b/l10n.toml index bb0d019f2..208d698a0 100644 --- a/l10n.toml +++ b/l10n.toml @@ -1,7 +1,6 @@ basepath = "." -# Locales that should be present in non-release builds and on Pontoon locales = [ "an", "ar", @@ -95,97 +94,6 @@ locales = [ "zh-TW", ] -# Locales that should be present in release builds -# Locales that are at 70% or higher completion on https://pontoon.mozilla.org/projects/android-l10n/ -# should be in this list -release_locales = [ - "an", - "ar", - "ast", - "az", - "be", - "bn", - "br", - "bs", - "ca", - "cak", - "co", - "cs", - "cy", - "da", - "de", - "dsb", - "el", - "en-CA", - "en-GB", - "eo", - "es", - "es-AR", - "es-CL", - "es-ES", - "es-MX", - "et", - "eu", - "fa", - "ff", - "fi", - "fr", - "fy-NL", - "ga-IE", - "gd", - "gn", - "gu-IN", - "he", - "hi-IN", - "hr", - "hsb", - "hu", - "hy-AM", - "id", - "is", - "it", - "ja", - "ka", - "kab", - "kk", - "kn", - "ko", - "lij", - "lo", - "lt", - "ml", - "mr", - "my", - "nb-NO", - "nl", - "nn-NO", - "oc", - "pa-IN", - "pl", - "pt-BR", - "pt-PT", - "rm", - "ro", - "ru", - "sk", - "sl", - "sq", - "sr", - "su", - "sv-SE", - "ta", - "te", - "th", - "tr", - "trs", - "uk", - "ur", - "vec", - "vi", - "zh-CN", - "zh-TW", -] - # Expose the following branches to localization # Changes to this list should be announced to the l10n team ahead of time. branches = [ From 8826f99ef7ce67b20e3e4458514860690188fc17 Mon Sep 17 00:00:00 2001 From: mcarare Date: Wed, 15 Jul 2020 16:14:29 +0300 Subject: [PATCH 25/31] For #9722: Refactor onboarding buttons with text. Add title and description to button text, remove extra Textviews. --- .../org/mozilla/fenix/ext/SpannableString.kt | 34 +++++++ .../OnboardingTrackingProtectionViewHolder.kt | 52 ++--------- .../fenix/onboarding/OnboardingRadioButton.kt | 36 +++++++ .../res/layout/onboarding_theme_picker.xml | 29 ++---- .../layout/onboarding_tracking_protection.xml | 93 +++++-------------- app/src/main/res/values/attrs.xml | 2 + 6 files changed, 110 insertions(+), 136 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt diff --git a/app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt b/app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt new file mode 100644 index 000000000..68a2b2e61 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.ext + +import android.content.Context +import android.text.Spannable +import android.text.SpannableString +import android.text.style.AbsoluteSizeSpan +import android.text.style.ForegroundColorSpan +import androidx.core.content.ContextCompat +import mozilla.components.support.ktx.android.util.dpToPx + +fun SpannableString.setTextSize(context: Context, textSize: Int) = + this.setSpan( + AbsoluteSizeSpan(textSize.dpToPx(context.resources.displayMetrics)), + 0, + this.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + +fun SpannableString.setTextColor(context: Context, colorResId: Int) = + this.setSpan( + ForegroundColorSpan( + ContextCompat.getColor( + context, + colorResId + ) + ), + 0, + this.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingTrackingProtectionViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingTrackingProtectionViewHolder.kt index 57eeea68f..eee57c745 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingTrackingProtectionViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingTrackingProtectionViewHolder.kt @@ -37,17 +37,17 @@ class OnboardingTrackingProtectionViewHolder(view: View) : RecyclerView.ViewHold isChecked = view.context.settings().shouldUseTrackingProtection setOnCheckedChangeListener { _, isChecked -> updateTrackingProtectionSetting(isChecked) - updateRadioGroupState(view, isChecked) + updateRadioGroupState(isChecked) } } - setupRadioGroup(view, trackingProtectionToggle.isChecked) - updateRadioGroupState(view, trackingProtectionToggle.isChecked) + setupRadioGroup(trackingProtectionToggle.isChecked) + updateRadioGroupState(trackingProtectionToggle.isChecked) } - private fun setupRadioGroup(view: View, isChecked: Boolean) { + private fun setupRadioGroup(isChecked: Boolean) { - updateRadioGroupState(view, isChecked) + updateRadioGroupState(isChecked) addToRadioGroup(standardTrackingProtection, strictTrackingProtection) @@ -58,56 +58,20 @@ class OnboardingTrackingProtectionViewHolder(view: View) : RecyclerView.ViewHold standardTrackingProtection.onClickListener { updateTrackingProtectionPolicy() - view.context.components.analytics.metrics + itemView.context.components.analytics.metrics .track(Event.OnboardingTrackingProtection(Setting.STANDARD)) } - view.clickable_region_standard.apply { - setOnClickListener { - standardTrackingProtection.performClick() - view.context.components.analytics.metrics - .track(Event.OnboardingTrackingProtection(Setting.STANDARD)) - } - val standardTitle = view.context.getString( - R.string.onboarding_tracking_protection_standard_button_2 - ) - val standardSummary = view.context.getString( - R.string.onboarding_tracking_protection_standard_button_description_2 - ) - contentDescription = "$standardTitle. $standardSummary" - } - strictTrackingProtection.onClickListener { updateTrackingProtectionPolicy() - view.context.components.analytics.metrics + itemView.context.components.analytics.metrics .track(Event.OnboardingTrackingProtection(Setting.STRICT)) } - - view.clickable_region_strict.apply { - setOnClickListener { - strictTrackingProtection.performClick() - view.context.components.analytics.metrics - .track(Event.OnboardingTrackingProtection(Setting.STRICT)) - } - val strictTitle = - view.context.getString(R.string.onboarding_tracking_protection_strict_option) - val strictSummary = - view.context.getString(R.string.onboarding_tracking_protection_strict_button_description_2) - contentDescription = "$strictTitle. $strictSummary" - } } - private fun updateRadioGroupState(view: View, isChecked: Boolean) { + private fun updateRadioGroupState(isChecked: Boolean) { standardTrackingProtection.isEnabled = isChecked strictTrackingProtection.isEnabled = isChecked - - view.protection_standard_description.isEnabled = isChecked - view.protection_strict_description.isEnabled = isChecked - view.clickable_region_standard.isClickable = isChecked - - view.protection_standard_title.isEnabled = isChecked - view.protection_strict_title.isEnabled = isChecked - view.clickable_region_strict.isClickable = isChecked } private fun updateTrackingProtectionSetting(enabled: Boolean) { diff --git a/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingRadioButton.kt b/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingRadioButton.kt index 697156f5b..4ed8d330c 100644 --- a/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingRadioButton.kt +++ b/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingRadioButton.kt @@ -5,12 +5,16 @@ package org.mozilla.fenix.onboarding import android.content.Context +import android.text.SpannableString +import android.text.SpannableStringBuilder import android.util.AttributeSet import android.widget.ImageView import androidx.appcompat.widget.AppCompatRadioButton import androidx.core.content.edit import androidx.core.content.withStyledAttributes import org.mozilla.fenix.R +import org.mozilla.fenix.ext.setTextColor +import org.mozilla.fenix.ext.setTextSize import org.mozilla.fenix.ext.settings import org.mozilla.fenix.utils.view.GroupableRadioButton import org.mozilla.fenix.utils.view.uncheckAll @@ -23,6 +27,8 @@ class OnboardingRadioButton( private var illustration: ImageView? = null private var clickListener: (() -> Unit)? = null var key: Int = 0 + var title: Int = 0 + var description: Int = 0 init { context.withStyledAttributes( @@ -31,6 +37,9 @@ class OnboardingRadioButton( 0, 0 ) { key = getResourceId(R.styleable.OnboardingRadioButton_onboardingKey, 0) + title = getResourceId(R.styleable.OnboardingRadioButton_onboardingKeyTitle, 0) + description = + getResourceId(R.styleable.OnboardingRadioButton_onboardingKeyDescription, 0) } } @@ -52,6 +61,28 @@ class OnboardingRadioButton( toggleRadioGroups() clickListener?.invoke() } + if (title != 0) { + setRadioButtonText(context) + } + } + + private fun setRadioButtonText(context: Context) { + val builder = SpannableStringBuilder() + + val spannableTitle = SpannableString(resources.getString(title)) + spannableTitle.setTextSize(context, TITLE_TEXT_SIZE) + spannableTitle.setTextColor(context, R.color.primary_state_list_text_color) + + builder.append(spannableTitle) + + if (description != 0) { + val spannableDescription = SpannableString(resources.getString(description)) + spannableDescription.setTextSize(context, DESCRIPTION_TEXT_SIZE) + spannableDescription.setTextColor(context, R.color.secondary_state_list_text_color) + builder.append("\n") + builder.append(spannableDescription) + } + this.text = builder } override fun updateRadioValue(isChecked: Boolean) { @@ -69,4 +100,9 @@ class OnboardingRadioButton( radioGroups.uncheckAll() } } + + companion object { + private const val TITLE_TEXT_SIZE = 16 + private const val DESCRIPTION_TEXT_SIZE = 14 + } } diff --git a/app/src/main/res/layout/onboarding_theme_picker.xml b/app/src/main/res/layout/onboarding_theme_picker.xml index 4b5cd64a1..bf6757c7b 100644 --- a/app/src/main/res/layout/onboarding_theme_picker.xml +++ b/app/src/main/res/layout/onboarding_theme_picker.xml @@ -4,6 +4,7 @@ - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + app:onboardingKey="@string/pref_key_follow_device_theme" + app:onboardingKeyDescription="@string/onboarding_theme_automatic_summary" + app:onboardingKeyTitle="@string/onboarding_theme_automatic_title" + tools:text="Automatic" /> - - - diff --git a/app/src/main/res/layout/onboarding_tracking_protection.xml b/app/src/main/res/layout/onboarding_tracking_protection.xml index 1bf3fcd85..78a7df377 100644 --- a/app/src/main/res/layout/onboarding_tracking_protection.xml +++ b/app/src/main/res/layout/onboarding_tracking_protection.xml @@ -7,7 +7,9 @@ android:id="@+id/onboarding_card" style="@style/OnboardingCardLightWithPadding" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:clipChildren="false" + android:clipToPadding="false"> - - - - - - - + app:onboardingKey="@string/pref_key_tracking_protection_standard_option" + app:onboardingKeyDescription="@string/onboarding_tracking_protection_standard_button_description_2" + app:onboardingKeyTitle="@string/onboarding_tracking_protection_standard_button_2" + tools:text="Standard" /> - - - - + app:onboardingKey="@string/pref_key_tracking_protection_strict_default" + app:onboardingKeyDescription="@string/onboarding_tracking_protection_strict_button_description_2" + app:onboardingKeyTitle="@string/onboarding_tracking_protection_strict_option" + tools:text="Strict" /> diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 881f04879..f3ed115f1 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -84,6 +84,8 @@ + + From c3041bcb648177d9cf0cb35ca32150375f35681e Mon Sep 17 00:00:00 2001 From: Mihai Eduard Badea Date: Tue, 21 Jul 2020 10:18:13 +0300 Subject: [PATCH 26/31] For issue #12387 - Display tab tray using .show Replaced the global navigation action used for displaying the tab tray with the .show() function. --- .../fenix/library/bookmarks/BookmarkFragment.kt | 10 ++++++++-- .../fenix/library/history/HistoryFragment.kt | 15 +++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkFragment.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkFragment.kt index 9b76c072f..b0bce78ee 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkFragment.kt @@ -51,6 +51,7 @@ import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.toShortUrl import org.mozilla.fenix.library.LibraryPageFragment +import org.mozilla.fenix.tabtray.TabTrayDialogFragment import org.mozilla.fenix.utils.allowUndo /** @@ -207,14 +208,14 @@ class BookmarkFragment : LibraryPageFragment(), UserInteractionHan R.id.open_bookmarks_in_new_tabs_multi_select -> { openItemsInNewTab { node -> node.url } - navigate(BookmarkFragmentDirections.actionGlobalTabTrayDialogFragment()) + showTabTray() metrics?.track(Event.OpenedBookmarksInNewTabs) true } R.id.open_bookmarks_in_private_tabs_multi_select -> { openItemsInNewTab(private = true) { node -> node.url } - navigate(BookmarkFragmentDirections.actionGlobalTabTrayDialogFragment()) + showTabTray() metrics?.track(Event.OpenedBookmarksInPrivateTabs) true } @@ -237,6 +238,11 @@ class BookmarkFragment : LibraryPageFragment(), UserInteractionHan } } + private fun showTabTray() { + invokePendingDeletion() + TabTrayDialogFragment.show(parentFragmentManager) + } + private fun navigate(directions: NavDirections) { invokePendingDeletion() findNavController().nav( diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt index a228d12c7..8b8179fa0 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt @@ -44,6 +44,7 @@ import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.ext.toShortUrl import org.mozilla.fenix.library.LibraryPageFragment +import org.mozilla.fenix.tabtray.TabTrayDialogFragment import org.mozilla.fenix.utils.allowUndo @SuppressWarnings("TooManyFunctions", "LargeClass") @@ -184,9 +185,7 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandl selectedItem.url } - navigate( - HistoryFragmentDirections.actionGlobalTabTrayDialogFragment() - ) + showTabTray() true } R.id.open_history_in_private_tabs_multi_select -> { @@ -199,14 +198,18 @@ class HistoryFragment : LibraryPageFragment(), UserInteractionHandl browsingModeManager.mode = BrowsingMode.Private supportActionBar?.hide() } - navigate( - HistoryFragmentDirections.actionGlobalTabTrayDialogFragment() - ) + + showTabTray() true } else -> super.onOptionsItemSelected(item) } + private fun showTabTray() { + invokePendingDeletion() + TabTrayDialogFragment.show(parentFragmentManager) + } + private fun getMultiSelectSnackBarMessage(historyItems: Set): String { return if (historyItems.size > 1) { getString(R.string.history_delete_multiple_items_snackbar) From c08d375c18ada667a28c4f103ba697a22c0a14a2 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Tue, 21 Jul 2020 10:47:10 -0700 Subject: [PATCH 27/31] Move settings in components (#12675) --- .../StrictEnhancedTrackingProtectionTest.kt | 2 +- .../org/mozilla/fenix/engine/GeckoProvider.kt | 7 +- .../org/mozilla/fenix/engine/GeckoProvider.kt | 7 +- .../org/mozilla/fenix/FenixApplication.kt | 4 +- .../java/org/mozilla/fenix/HomeActivity.kt | 7 +- .../addons/InstalledAddonDetailsFragment.kt | 4 +- .../browsingmode/BrowsingModeManager.kt | 3 +- .../fenix/components/BackgroundServices.kt | 11 +-- .../mozilla/fenix/components/Components.kt | 3 + .../components/toolbar/DefaultToolbarMenu.kt | 5 +- .../java/org/mozilla/fenix/ext/Context.kt | 6 +- .../java/org/mozilla/fenix/home/HomeMenu.kt | 54 ++++++--------- ...boardingToolbarPositionPickerViewHolder.kt | 18 +++-- .../org/mozilla/fenix/perf/Performance.kt | 6 +- .../QuickSettingsSheetDialogFragment.kt | 14 ++-- .../TrackingProtectionOverlay.kt | 3 +- .../java/org/mozilla/fenix/utils/Settings.kt | 30 +++----- .../org/mozilla/fenix/HomeActivityTest.kt | 24 +++---- .../DefaultBrowsingModeManagerTest.kt | 11 ++- .../components/BackgroundServicesTest.kt | 38 +++++------ .../fenix/components/TestComponents.kt | 3 + .../DefaultBrowserToolbarControllerTest.kt | 10 ++- ...dingToolbarPositionPickerViewHolderTest.kt | 13 +--- .../search/DefaultSearchControllerTest.kt | 26 ++----- .../deletebrowsingdata/DeleteAndQuitTest.kt | 19 ++---- .../org/mozilla/fenix/utils/SettingsTest.kt | 68 +++++-------------- .../fenix/whatsnew/WhatsNewStorageTest.kt | 13 ++-- .../mozilla/fenix/whatsnew/WhatsNewTest.kt | 10 +-- 28 files changed, 169 insertions(+), 250 deletions(-) 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 5ac044f6c..86f5c929f 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt @@ -46,7 +46,7 @@ class StrictEnhancedTrackingProtectionTest { start() } - InstrumentationRegistry.getInstrumentation().context.settings().setStrictETP() + activityTestRule.activity.settings().setStrictETP() // Reset on-boarding notification for each test TestHelper.setPreference( diff --git a/app/src/geckoBeta/java/org/mozilla/fenix/engine/GeckoProvider.kt b/app/src/geckoBeta/java/org/mozilla/fenix/engine/GeckoProvider.kt index fc13e5b00..96aa0d767 100644 --- a/app/src/geckoBeta/java/org/mozilla/fenix/engine/GeckoProvider.kt +++ b/app/src/geckoBeta/java/org/mozilla/fenix/engine/GeckoProvider.kt @@ -10,7 +10,7 @@ import mozilla.components.concept.storage.LoginsStorage import mozilla.components.lib.crash.handler.CrashHandlerService import mozilla.components.service.sync.logins.GeckoLoginStorageDelegate import org.mozilla.fenix.Config -import org.mozilla.fenix.utils.Settings +import org.mozilla.fenix.ext.components import org.mozilla.geckoview.GeckoRuntime import org.mozilla.geckoview.GeckoRuntimeSettings @@ -48,9 +48,10 @@ object GeckoProvider { .debugLogging(Config.channel.isDebug) .build() - if (!Settings.getInstance(context).shouldUseAutoSize) { + val settings = context.components.settings + if (!settings.shouldUseAutoSize) { runtimeSettings.automaticFontSizeAdjustment = false - val fontSize = Settings.getInstance(context).fontSizeFactor + val fontSize = settings.fontSizeFactor runtimeSettings.fontSizeFactor = fontSize } diff --git a/app/src/geckoNightly/java/org/mozilla/fenix/engine/GeckoProvider.kt b/app/src/geckoNightly/java/org/mozilla/fenix/engine/GeckoProvider.kt index c5a27fa63..2508f6f57 100644 --- a/app/src/geckoNightly/java/org/mozilla/fenix/engine/GeckoProvider.kt +++ b/app/src/geckoNightly/java/org/mozilla/fenix/engine/GeckoProvider.kt @@ -10,7 +10,7 @@ import mozilla.components.concept.storage.LoginsStorage import mozilla.components.lib.crash.handler.CrashHandlerService import mozilla.components.service.sync.logins.GeckoLoginStorageDelegate import org.mozilla.fenix.Config -import org.mozilla.fenix.utils.Settings +import org.mozilla.fenix.ext.components import org.mozilla.geckoview.GeckoRuntime import org.mozilla.geckoview.GeckoRuntimeSettings @@ -48,9 +48,10 @@ object GeckoProvider { .aboutConfigEnabled(true) .build() - if (!Settings.getInstance(context).shouldUseAutoSize) { + val settings = context.components.settings + if (!settings.shouldUseAutoSize) { runtimeSettings.automaticFontSizeAdjustment = false - val fontSize = Settings.getInstance(context).fontSizeFactor + val fontSize = settings.fontSizeFactor runtimeSettings.fontSizeFactor = fontSize } diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 6089fc991..b91e40fca 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -47,7 +47,6 @@ import org.mozilla.fenix.push.WebPushEngineIntegration import org.mozilla.fenix.session.PerformanceActivityLifecycleCallbacks import org.mozilla.fenix.session.VisibilityLifecycleCallback import org.mozilla.fenix.utils.BrowsersCache -import org.mozilla.fenix.utils.Settings /** *The main application class for Fenix. Records data to measure initialization performance. @@ -355,8 +354,7 @@ open class FenixApplication : LocaleAwareApplication() { _, engineSession, url -> val shouldCreatePrivateSession = components.core.sessionManager.selectedSession?.private - ?: Settings.instance?.openLinksInAPrivateTab - ?: false + ?: components.settings.openLinksInAPrivateTab val session = Session(url, shouldCreatePrivateSession) components.core.sessionManager.add(session, true, engineSession) diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index dcbc6e51c..0efa5e962 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -26,7 +26,6 @@ import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.NavigationUI import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.gms.tasks.Tasks.call import kotlinx.android.synthetic.main.activity_home.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -65,7 +64,6 @@ import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager import org.mozilla.fenix.browser.browsingmode.DefaultBrowsingModeManager import org.mozilla.fenix.components.metrics.BreadcrumbsRecorder import org.mozilla.fenix.components.metrics.Event -import org.mozilla.fenix.trackingprotectionexceptions.TrackingProtectionExceptionsFragmentDirections import org.mozilla.fenix.ext.alreadyOnDestination import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.nav @@ -87,8 +85,8 @@ import org.mozilla.fenix.session.NotificationSessionObserver import org.mozilla.fenix.settings.SettingsFragmentDirections import org.mozilla.fenix.settings.TrackingProtectionFragmentDirections import org.mozilla.fenix.settings.about.AboutFragmentDirections -import org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragmentDirections import org.mozilla.fenix.settings.logins.fragment.LoginDetailFragmentDirections +import org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragmentDirections import org.mozilla.fenix.settings.search.AddSearchEngineFragmentDirections import org.mozilla.fenix.settings.search.EditCustomSearchEngineFragmentDirections import org.mozilla.fenix.share.AddNewDeviceFragmentDirections @@ -97,6 +95,7 @@ import org.mozilla.fenix.tabtray.FenixTabsAdapter import org.mozilla.fenix.tabtray.TabTrayDialogFragment import org.mozilla.fenix.theme.DefaultThemeManager import org.mozilla.fenix.theme.ThemeManager +import org.mozilla.fenix.trackingprotectionexceptions.TrackingProtectionExceptionsFragmentDirections import org.mozilla.fenix.utils.BrowsersCache import org.mozilla.fenix.utils.RunWhenReadyQueue @@ -575,7 +574,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { } protected open fun createBrowsingModeManager(initialMode: BrowsingMode): BrowsingModeManager { - return DefaultBrowsingModeManager(initialMode) { newMode -> + return DefaultBrowsingModeManager(initialMode, components.settings) { newMode -> themeManager.currentTheme = newMode } } 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 3a946c236..630b7592b 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt @@ -25,7 +25,6 @@ import mozilla.components.feature.addons.ui.translatedName import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.showToolbar -import org.mozilla.fenix.utils.Settings /** * An activity to show the details of a installed add-on. @@ -191,8 +190,7 @@ class InstalledAddonDetailsFragment : Fragment() { val components = it.context.components val shouldCreatePrivateSession = components.core.store.state.selectedTab?.content?.private - ?: Settings.instance?.openLinksInAPrivateTab - ?: false + ?: components.settings.openLinksInAPrivateTab if (shouldCreatePrivateSession) { components.useCases.tabsUseCases.addPrivateTab(settingUrl) diff --git a/app/src/main/java/org/mozilla/fenix/browser/browsingmode/BrowsingModeManager.kt b/app/src/main/java/org/mozilla/fenix/browser/browsingmode/BrowsingModeManager.kt index cfe32f6f3..f117b4521 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/browsingmode/BrowsingModeManager.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/browsingmode/BrowsingModeManager.kt @@ -36,6 +36,7 @@ interface BrowsingModeManager { */ class DefaultBrowsingModeManager( private var _mode: BrowsingMode, + private val settings: Settings, private val modeDidChange: (BrowsingMode) -> Unit ) : BrowsingModeManager { @@ -44,6 +45,6 @@ class DefaultBrowsingModeManager( set(value) { _mode = value modeDidChange(value) - Settings.instance?.lastKnownMode = value + settings.lastKnownMode = value } } diff --git a/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt b/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt index f24315a08..feb74728c 100644 --- a/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt +++ b/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt @@ -40,6 +40,7 @@ import org.mozilla.fenix.ext.settings import org.mozilla.fenix.sync.SyncedTabsIntegration import org.mozilla.fenix.utils.Mockable import org.mozilla.fenix.utils.RunWhenReadyQueue +import org.mozilla.fenix.utils.Settings /** * Component group for background services. These are the components that need to be accessed from within a @@ -103,7 +104,7 @@ class BackgroundServices( } private val telemetryAccountObserver = TelemetryAccountObserver( - context, + context.settings(), context.components.analytics.metrics ) @@ -178,8 +179,8 @@ class BackgroundServices( } @VisibleForTesting(otherwise = PRIVATE) -class TelemetryAccountObserver( - private val context: Context, +internal class TelemetryAccountObserver( + private val settings: Settings, private val metricController: MetricController ) : AccountObserver { override fun onAuthenticated(account: OAuthAccount, authType: AuthType) { @@ -211,12 +212,12 @@ class TelemetryAccountObserver( metricController.track(Event.SyncAuthOtherExternal) } // Used by Leanplum as a context variable. - context.settings().fxaSignedIn = true + settings.fxaSignedIn = true } override fun onLoggedOut() { metricController.track(Event.SyncAuthSignOut) // Used by Leanplum as a context variable. - context.settings().fxaSignedIn = false + settings.fxaSignedIn = false } } diff --git a/app/src/main/java/org/mozilla/fenix/components/Components.kt b/app/src/main/java/org/mozilla/fenix/components/Components.kt index 719adfd39..eec2fccfe 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Components.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Components.kt @@ -20,6 +20,7 @@ import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.utils.ClipboardHandler import org.mozilla.fenix.utils.Mockable +import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.wifi.WifiConnectionMonitor import java.util.concurrent.TimeUnit @@ -107,4 +108,6 @@ class Components(private val context: Context) { val performance by lazy { PerformanceComponent() } val push by lazy { Push(context, analytics.crashReporter) } val wifiConnectionMonitor by lazy { WifiConnectionMonitor(context.getSystemService()!!) } + + val settings by lazy { Settings(context) } } diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt index 427ec0306..a27691bac 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/DefaultToolbarMenu.kt @@ -24,15 +24,14 @@ import mozilla.components.browser.state.selector.findTab import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.storage.BookmarksStorage import mozilla.components.support.ktx.android.content.getColorFromAttr -import org.mozilla.fenix.R import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.R import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.ext.asActivity import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.theme.ThemeManager -import org.mozilla.fenix.utils.Settings /** * Builds the toolbar object used with the 3-dot menu in the browser fragment. @@ -160,7 +159,7 @@ class DefaultToolbarMenu( // Predicates that are called once, during screen init val shouldShowSaveToCollection = (context.asActivity() as? HomeActivity) ?.browsingModeManager?.mode == BrowsingMode.Normal - val shouldDeleteDataOnQuit = Settings.getInstance(context) + val shouldDeleteDataOnQuit = context.components.settings .shouldDeleteBrowsingDataOnQuit val menuItems = listOfNotNull( diff --git a/app/src/main/java/org/mozilla/fenix/ext/Context.kt b/app/src/main/java/org/mozilla/fenix/ext/Context.kt index 6331d98f9..b9f60dafe 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/Context.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/Context.kt @@ -12,13 +12,10 @@ import android.view.ViewGroup import androidx.annotation.StringRes import mozilla.components.browser.search.SearchEngineManager import mozilla.components.support.locale.LocaleManager -import org.mozilla.fenix.BuildConfig -import org.mozilla.fenix.Config import org.mozilla.fenix.FenixApplication import org.mozilla.fenix.components.Components import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.settings.advanced.getSelectedLocale -import org.mozilla.fenix.utils.Settings import java.lang.String.format import java.util.Locale @@ -60,8 +57,7 @@ fun Context.getPreferenceKey(@StringRes resourceId: Int): String = fun Context.getRootView(): View? = asActivity()?.window?.decorView?.findViewById(android.R.id.content) as? ViewGroup -fun Context.settings(isCrashReportEnabledInBuild: Boolean = BuildConfig.CRASH_REPORTING && Config.channel.isReleased) = - Settings.getInstance(this, isCrashReportEnabledInBuild) +fun Context.settings() = components.settings /** * Used to catch IllegalArgumentException that is thrown when diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeMenu.kt b/app/src/main/java/org/mozilla/fenix/home/HomeMenu.kt index c38edf498..1ab68040b 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeMenu.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeMenu.kt @@ -26,7 +26,6 @@ import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.theme.ThemeManager -import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.whatsnew.WhatsNew class HomeMenu( @@ -153,40 +152,29 @@ class HomeMenu( null } + val settings = context.components.settings + + val menuItems = listOfNotNull( + if (settings.shouldDeleteBrowsingDataOnQuit) quitItem else null, + settingsItem, + BrowserMenuDivider(), + if (FeatureFlags.syncedTabs) syncedTabsItem else null, + bookmarksItem, + historyItem, + BrowserMenuDivider(), + addons, + BrowserMenuDivider(), + whatsNewItem, + helpItem, + accountAuthItem + ).also { items -> + items.getHighlight()?.let { onHighlightPresent(it) } + } + if (shouldUseBottomToolbar) { - listOfNotNull( - accountAuthItem, - helpItem, - whatsNewItem, - BrowserMenuDivider(), - addons, - BrowserMenuDivider(), - historyItem, - bookmarksItem, - if (FeatureFlags.syncedTabs) syncedTabsItem else null, - BrowserMenuDivider(), - settingsItem, - if (Settings.getInstance(context).shouldDeleteBrowsingDataOnQuit) quitItem else null - ).also { items -> - items.getHighlight()?.let { onHighlightPresent(it) } - } + menuItems.reversed() } else { - listOfNotNull( - if (Settings.getInstance(context).shouldDeleteBrowsingDataOnQuit) quitItem else null, - settingsItem, - BrowserMenuDivider(), - if (FeatureFlags.syncedTabs) syncedTabsItem else null, - bookmarksItem, - historyItem, - BrowserMenuDivider(), - addons, - BrowserMenuDivider(), - whatsNewItem, - helpItem, - accountAuthItem - ).also { items -> - items.getHighlight()?.let { onHighlightPresent(it) } - } + menuItems } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolder.kt index f15749335..87c3453ee 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolder.kt @@ -12,12 +12,13 @@ import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event.OnboardingToolbarPosition.Position import org.mozilla.fenix.ext.asActivity import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.settings import org.mozilla.fenix.onboarding.OnboardingRadioButton import org.mozilla.fenix.utils.view.addToRadioGroup class OnboardingToolbarPositionPickerViewHolder(view: View) : RecyclerView.ViewHolder(view) { + private val metrics = view.context.components.analytics.metrics + init { val radioTopToolbar = view.toolbar_top_radio_button val radioBottomToolbar = view.toolbar_bottom_radio_button @@ -27,7 +28,8 @@ class OnboardingToolbarPositionPickerViewHolder(view: View) : RecyclerView.ViewH radioTopToolbar.addIllustration(view.toolbar_top_image) radioBottomToolbar.addIllustration(view.toolbar_bottom_image) - radio = if (view.context.settings().shouldUseBottomToolbar) { + val settings = view.context.components.settings + radio = if (settings.shouldUseBottomToolbar) { radioBottomToolbar } else { radioTopToolbar @@ -35,28 +37,24 @@ class OnboardingToolbarPositionPickerViewHolder(view: View) : RecyclerView.ViewH radio.updateRadioValue(true) radioBottomToolbar.onClickListener { - itemView.context.components.analytics.metrics - .track(Event.OnboardingToolbarPosition(Position.BOTTOM)) + metrics.track(Event.OnboardingToolbarPosition(Position.BOTTOM)) itemView.context.asActivity()?.recreate() } view.toolbar_bottom_image.setOnClickListener { - itemView.context.components.analytics.metrics - .track(Event.OnboardingToolbarPosition(Position.BOTTOM)) + metrics.track(Event.OnboardingToolbarPosition(Position.BOTTOM)) radioBottomToolbar.performClick() } radioTopToolbar.onClickListener { - itemView.context.components.analytics.metrics - .track(Event.OnboardingToolbarPosition(Position.TOP)) + metrics.track(Event.OnboardingToolbarPosition(Position.TOP)) itemView.context.asActivity()?.recreate() } view.toolbar_top_image.setOnClickListener { - itemView.context.components.analytics.metrics - .track(Event.OnboardingToolbarPosition(Position.TOP)) + metrics.track(Event.OnboardingToolbarPosition(Position.TOP)) radioTopToolbar.performClick() } } diff --git a/app/src/main/java/org/mozilla/fenix/perf/Performance.kt b/app/src/main/java/org/mozilla/fenix/perf/Performance.kt index f1eb26ffb..410295577 100644 --- a/app/src/main/java/org/mozilla/fenix/perf/Performance.kt +++ b/app/src/main/java/org/mozilla/fenix/perf/Performance.kt @@ -8,8 +8,8 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.BatteryManager +import org.mozilla.fenix.ext.components import org.mozilla.fenix.onboarding.FenixOnboarding -import org.mozilla.fenix.utils.Settings import android.provider.Settings as AndroidSettings /** @@ -67,13 +67,13 @@ object Performance { * Disables the tracking protection popup. However, TP is still on. */ private fun disableTrackingProtectionPopups(context: Context) { - Settings.getInstance(context).isOverrideTPPopupsForPerformanceTest = true + context.components.settings.isOverrideTPPopupsForPerformanceTest = true } /** * Disables the first time PWA popup. */ private fun disableFirstTimePWAPopup(context: Context) { - Settings.getInstance(context).userKnowsAboutPwas = true + context.components.settings.userKnowsAboutPwas = true } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt index adfdbfee8..d38591b87 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsSheetDialogFragment.kt @@ -37,7 +37,6 @@ import org.mozilla.fenix.IntentReceiverActivity import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.settings.PhoneFeature -import org.mozilla.fenix.utils.Settings import com.google.android.material.R as MaterialR /** @@ -62,6 +61,7 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { savedInstanceState: Bundle? ): View { val context = requireContext() + val components = context.components val rootView = inflateRootView(container) quickSettingsStore = QuickSettingsFragmentStore.createStore( @@ -70,7 +70,7 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { websiteTitle = args.title, isSecured = args.isSecured, permissions = args.sitePermissions, - settings = Settings.getInstance(context), + settings = components.settings, certificateName = args.certificateName ) @@ -79,12 +79,12 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment() { quickSettingsStore = quickSettingsStore, ioScope = viewLifecycleOwner.lifecycleScope + Dispatchers.IO, navController = findNavController(), - session = context.components.core.sessionManager.findSessionById(args.sessionId), + session = components.core.sessionManager.findSessionById(args.sessionId), sitePermissions = args.sitePermissions, - settings = Settings.getInstance(context), - permissionStorage = context.components.core.permissionStorage, - reload = context.components.useCases.sessionUseCases.reload, - addNewTab = context.components.useCases.tabsUseCases.addTab, + settings = components.settings, + permissionStorage = components.core.permissionStorage, + reload = components.useCases.sessionUseCases.reload, + addNewTab = components.useCases.tabsUseCases.addTab, requestRuntimePermissions = { permissions -> requestPermissions(permissions, REQUEST_CODE_QUICK_SETTINGS_PERMISSIONS) tryToRequestPermissions = true diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt index 194c1326d..91916c768 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt @@ -22,7 +22,6 @@ import mozilla.components.browser.session.Session import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController -import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.increaseTapArea import org.mozilla.fenix.utils.Settings @@ -56,7 +55,7 @@ class TrackingProtectionOverlay( override fun onTouchEvent(event: MotionEvent): Boolean { if (event.action == MotionEvent.ACTION_DOWN) { - context.components.analytics.metrics.track(Event.ContextualHintETPOutsideTap) + metrics.track(Event.ContextualHintETPOutsideTap) } return super.onTouchEvent(event) } 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 a76825bef..cb88d7579 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -33,9 +33,9 @@ import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getPreferenceKey import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.deletebrowsingdata.DeleteBrowsingDataOnQuitType -import org.mozilla.fenix.settings.logins.fragment.SavedLoginsFragment import org.mozilla.fenix.settings.logins.SavedLoginsSortingStrategyMenu import org.mozilla.fenix.settings.logins.SortingStrategy +import org.mozilla.fenix.settings.logins.fragment.SavedLoginsFragment import org.mozilla.fenix.settings.registerOnSharedPreferenceChangeListener import java.security.InvalidParameterException @@ -43,12 +43,11 @@ private const val AUTOPLAY_USER_SETTING = "AUTOPLAY_USER_SETTING" /** * A simple wrapper for SharedPreferences that makes reading preference a little bit easier. + * @param appContext Reference to application context. */ @Suppress("LargeClass", "TooManyFunctions") -class Settings private constructor( - context: Context, - private val isCrashReportEnabledInBuild: Boolean -) : PreferencesHolder { +class Settings(private val appContext: Context) : PreferencesHolder { + companion object { const val showLoginsSecureWarningSyncMaxCount = 1 const val showLoginsSecureWarningMaxCount = 1 @@ -88,24 +87,11 @@ class Settings private constructor( ASK_TO_ALLOW_INT -> AutoplayAction.BLOCKED else -> throw InvalidParameterException("$this is not a valid SitePermissionsRules.AutoplayAction") } - - @VisibleForTesting - internal var instance: Settings? = null - - @JvmStatic - @Synchronized - fun getInstance( - context: Context, - isCrashReportEnabledInBuild: Boolean = BuildConfig.CRASH_REPORTING && Config.channel.isReleased - ): Settings { - if (instance == null) { - instance = Settings(context.applicationContext, isCrashReportEnabledInBuild) - } - return instance ?: throw AssertionError("Instance cleared") - } } - private val appContext = context.applicationContext + @VisibleForTesting + internal val isCrashReportEnabledInBuild: Boolean = + BuildConfig.CRASH_REPORTING && Config.channel.isReleased override val preferences: SharedPreferences = appContext.getSharedPreferences(FENIX_PREFERENCES, MODE_PRIVATE) @@ -372,7 +358,7 @@ class Settings private constructor( appContext.getPreferenceKey(R.string.pref_key_tracking_protection_standard_option), false ).apply() - appContext?.components?.let { + appContext.components.let { val policy = it.core.trackingProtectionPolicyFactory .createTrackingProtectionPolicy() it.useCases.settingsUseCases.updateTrackingProtection.invoke(policy) diff --git a/app/src/test/java/org/mozilla/fenix/HomeActivityTest.kt b/app/src/test/java/org/mozilla/fenix/HomeActivityTest.kt index 1b45dc5df..6f5c9634c 100644 --- a/app/src/test/java/org/mozilla/fenix/HomeActivityTest.kt +++ b/app/src/test/java/org/mozilla/fenix/HomeActivityTest.kt @@ -6,6 +6,8 @@ package org.mozilla.fenix import android.content.Intent import android.os.Bundle +import io.mockk.every +import io.mockk.spyk import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.utils.toSafeIntent import org.junit.Assert.assertEquals @@ -13,6 +15,7 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNull import org.junit.Assert.assertTrue +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.HomeActivity.Companion.PRIVATE_BROWSING_MODE @@ -24,10 +27,17 @@ import org.mozilla.fenix.helpers.FenixRobolectricTestRunner @RunWith(FenixRobolectricTestRunner::class) class HomeActivityTest { + private lateinit var activity: HomeActivity + + @Before + fun setup() { + activity = spyk(HomeActivity()) + + every { activity.applicationContext } returns testContext + } + @Test fun getIntentSource() { - val activity = HomeActivity() - val launcherIntent = Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }.toSafeIntent() @@ -42,8 +52,6 @@ class HomeActivityTest { @Test fun `getModeFromIntentOrLastKnown returns mode from settings when intent does not set`() { - val activity = HomeActivity() - testContext.settings().lastKnownMode = BrowsingMode.Private assertEquals(testContext.settings().lastKnownMode, activity.getModeFromIntentOrLastKnown(null)) @@ -51,8 +59,6 @@ class HomeActivityTest { @Test fun `getModeFromIntentOrLastKnown returns mode from intent when set`() { - val activity = HomeActivity() - testContext.settings().lastKnownMode = BrowsingMode.Normal val intent = Intent() @@ -64,21 +70,16 @@ class HomeActivityTest { @Test fun `isActivityColdStarted returns true for null savedInstanceState and not launched from history`() { - val activity = HomeActivity() - assertTrue(activity.isActivityColdStarted(Intent(), null)) } @Test fun `isActivityColdStarted returns false for valid savedInstanceState and not launched from history`() { - val activity = HomeActivity() - assertFalse(activity.isActivityColdStarted(Intent(), Bundle())) } @Test fun `isActivityColdStarted returns false for null savedInstanceState and launched from history`() { - val activity = HomeActivity() val startingIntent = Intent().apply { flags = flags or Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY } @@ -88,7 +89,6 @@ class HomeActivityTest { @Test fun `isActivityColdStarted returns false for null savedInstanceState and not launched from history`() { - val activity = HomeActivity() val startingIntent = Intent().apply { flags = flags or Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY } diff --git a/app/src/test/java/org/mozilla/fenix/browser/browsingmode/DefaultBrowsingModeManagerTest.kt b/app/src/test/java/org/mozilla/fenix/browser/browsingmode/DefaultBrowsingModeManagerTest.kt index 8313debc7..802997826 100644 --- a/app/src/test/java/org/mozilla/fenix/browser/browsingmode/DefaultBrowsingModeManagerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/browser/browsingmode/DefaultBrowsingModeManagerTest.kt @@ -5,14 +5,19 @@ package org.mozilla.fenix.browser.browsingmode import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.every import io.mockk.impl.annotations.MockK +import io.mockk.just import io.mockk.verify import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test +import org.mozilla.fenix.utils.Settings class DefaultBrowsingModeManagerTest { + @MockK lateinit var settings: Settings @MockK(relaxed = true) lateinit var callback: (BrowsingMode) -> Unit lateinit var manager: BrowsingModeManager @@ -21,7 +26,9 @@ class DefaultBrowsingModeManagerTest { @Before fun before() { MockKAnnotations.init(this) - manager = DefaultBrowsingModeManager(initMode, callback) + + manager = DefaultBrowsingModeManager(initMode, settings, callback) + every { settings.lastKnownMode = any() } just Runs } @Test @@ -46,8 +53,10 @@ class DefaultBrowsingModeManagerTest { manager.mode = BrowsingMode.Private assertEquals(BrowsingMode.Private, manager.mode) + verify { settings.lastKnownMode = BrowsingMode.Private } manager.mode = BrowsingMode.Normal assertEquals(BrowsingMode.Normal, manager.mode) + verify { settings.lastKnownMode = BrowsingMode.Normal } } } diff --git a/app/src/test/java/org/mozilla/fenix/components/BackgroundServicesTest.kt b/app/src/test/java/org/mozilla/fenix/components/BackgroundServicesTest.kt index 9b989fd5b..ff9af2fe6 100644 --- a/app/src/test/java/org/mozilla/fenix/components/BackgroundServicesTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/BackgroundServicesTest.kt @@ -4,43 +4,43 @@ package org.mozilla.fenix.components -import android.content.Context +import io.mockk.MockKAnnotations import io.mockk.Runs import io.mockk.every +import io.mockk.impl.annotations.MockK import io.mockk.just import io.mockk.mockk import io.mockk.verify import mozilla.components.concept.sync.AccountObserver import mozilla.components.concept.sync.AuthType import mozilla.components.concept.sync.OAuthAccount -import mozilla.components.service.fxa.DeviceConfig -import mozilla.components.service.fxa.ServerConfig -import mozilla.components.service.fxa.SyncConfig -import mozilla.components.service.fxa.manager.FxaAccountManager import mozilla.components.support.base.observer.ObserverRegistry +import org.junit.Before import org.junit.Test import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController +import org.mozilla.fenix.utils.Settings class BackgroundServicesTest { - class TestableBackgroundServices( - val context: Context - ) : BackgroundServices(context, mockk(), mockk(), mockk(), mockk(), mockk(), mockk()) { - override fun makeAccountManager( - context: Context, - serverConfig: ServerConfig, - deviceConfig: DeviceConfig, - syncConfig: SyncConfig? - ) = mockk(relaxed = true) + + @MockK private lateinit var metrics: MetricController + @MockK private lateinit var settings: Settings + + private lateinit var observer: TelemetryAccountObserver + private lateinit var registry: ObserverRegistry + + @Before + fun setup() { + MockKAnnotations.init(this) + every { metrics.track(any()) } just Runs + every { settings.fxaSignedIn = any() } just Runs + + observer = TelemetryAccountObserver(mockk(relaxed = true), metrics) + registry = ObserverRegistry().apply { register(observer) } } @Test fun `telemetry account observer`() { - val metrics = mockk() - every { metrics.track(any()) } just Runs - val observer = TelemetryAccountObserver(mockk(relaxed = true), metrics) - val registry = ObserverRegistry() - registry.register(observer) val account = mockk() // Sign-in diff --git a/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt b/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt index 8822d2d4c..96c5bb86f 100644 --- a/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt +++ b/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt @@ -7,6 +7,7 @@ package org.mozilla.fenix.components import android.content.Context import io.mockk.mockk import org.mozilla.fenix.utils.ClipboardHandler +import org.mozilla.fenix.utils.Settings class TestComponents(private val context: Context) : Components(context) { override val backgroundServices by lazy { @@ -29,4 +30,6 @@ class TestComponents(private val context: Context) : Components(context) { override val analytics by lazy { Analytics(context) } override val clipboardHandler by lazy { ClipboardHandler(context) } + + override val settings by lazy { mockk(relaxed = true) } } diff --git a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt index 2a1cbd089..20fb905e1 100644 --- a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarControllerTest.kt @@ -543,7 +543,10 @@ class DefaultBrowserToolbarControllerTest { @Test fun handleToolbarNewTabPress() = runBlockingTest { - val browsingModeManager: BrowsingModeManager = DefaultBrowsingModeManager(BrowsingMode.Private) {} + val browsingModeManager: BrowsingModeManager = DefaultBrowsingModeManager( + BrowsingMode.Private, + mockk(relaxed = true) + ) {} val item = TabCounterMenuItem.NewTab(false) every { activity.browsingModeManager } returns browsingModeManager @@ -556,7 +559,10 @@ class DefaultBrowserToolbarControllerTest { @Test fun handleToolbarNewPrivateTabPress() = runBlockingTest { - val browsingModeManager: BrowsingModeManager = DefaultBrowsingModeManager(BrowsingMode.Normal) {} + val browsingModeManager: BrowsingModeManager = DefaultBrowsingModeManager( + BrowsingMode.Normal, + mockk(relaxed = true) + ) {} val item = TabCounterMenuItem.NewTab(true) every { activity.browsingModeManager } returns browsingModeManager diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolderTest.kt index ef4361de2..93636ec77 100644 --- a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolderTest.kt @@ -7,15 +7,14 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding import android.view.LayoutInflater import android.view.View import io.mockk.every -import io.mockk.mockk import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.* import mozilla.components.support.test.robolectric.testContext -import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mozilla.fenix.ext.components import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.utils.Settings @@ -27,16 +26,10 @@ class OnboardingToolbarPositionPickerViewHolderTest { @Before fun setup() { + val components = testContext.components view = LayoutInflater.from(testContext) .inflate(OnboardingToolbarPositionPickerViewHolder.LAYOUT_ID, null) - settings = mockk(relaxed = true) - - Settings.instance = settings - } - - @After - fun teardown() { - Settings.instance = null + settings = components.settings } @Test diff --git a/app/src/test/java/org/mozilla/fenix/search/DefaultSearchControllerTest.kt b/app/src/test/java/org/mozilla/fenix/search/DefaultSearchControllerTest.kt index cd9b08ffe..e472b357d 100644 --- a/app/src/test/java/org/mozilla/fenix/search/DefaultSearchControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/search/DefaultSearchControllerTest.kt @@ -35,7 +35,6 @@ import org.mozilla.fenix.ext.settings import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.settings.SupportUtils import org.mozilla.fenix.utils.Settings -import org.mozilla.fenix.whatsnew.clear @ExperimentalCoroutinesApi @RunWith(FenixRobolectricTestRunner::class) @@ -48,10 +47,10 @@ class DefaultSearchControllerTest { private val searchEngine: SearchEngine = mockk(relaxed = true) private val metrics: MetricController = mockk(relaxed = true) private val sessionManager: SessionManager = mockk(relaxed = true) + private val settings: Settings = mockk(relaxed = true) private val clearToolbarFocus: (() -> Unit) = mockk(relaxed = true) private lateinit var controller: DefaultSearchController - private lateinit var settings: Settings @Before fun setUp() { @@ -60,6 +59,7 @@ class DefaultSearchControllerTest { every { store.state.searchEngineSource.searchEngine } returns searchEngine every { activity.metrics } returns metrics every { activity.components.core.sessionManager } returns sessionManager + every { activity.components.settings } returns settings controller = DefaultSearchController( activity = activity, @@ -67,8 +67,6 @@ class DefaultSearchControllerTest { navController = navController, clearToolbarFocus = clearToolbarFocus ) - - settings = testContext.settings().apply { testContext.settings().clear() } } @Test @@ -154,10 +152,7 @@ class DefaultSearchControllerTest { @Test fun `show search shortcuts when setting enabled AND query empty`() { val text = "" - testContext.settings().preferences - .edit() - .putBoolean(testContext.getString(R.string.pref_key_show_search_shortcuts), true) - .apply() + every { settings.shouldShowSearchShortcuts } returns true controller.handleTextChanged(text) @@ -168,10 +163,7 @@ class DefaultSearchControllerTest { fun `show search shortcuts when setting enabled AND query equals url`() { val text = "mozilla.org" every { store.state.url } returns "mozilla.org" - testContext.settings().preferences - .edit() - .putBoolean(testContext.getString(R.string.pref_key_show_search_shortcuts), true) - .apply() + every { settings.shouldShowSearchShortcuts } returns true controller.handleTextChanged(text) @@ -189,10 +181,7 @@ class DefaultSearchControllerTest { @Test fun `do not show search shortcuts when setting disabled AND query empty AND url not matching query`() { - testContext.settings().preferences - .edit() - .putBoolean(testContext.getString(R.string.pref_key_show_search_shortcuts), false) - .apply() + every { settings.shouldShowSearchShortcuts } returns false assertFalse(testContext.settings().shouldShowSearchShortcuts) @@ -205,10 +194,7 @@ class DefaultSearchControllerTest { @Test fun `do not show search shortcuts when setting disabled AND query non-empty`() { - testContext.settings().preferences - .edit() - .putBoolean(testContext.getString(R.string.pref_key_show_search_shortcuts), false) - .apply() + every { settings.shouldShowSearchShortcuts } returns false assertFalse(testContext.settings().shouldShowSearchShortcuts) diff --git a/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteAndQuitTest.kt b/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteAndQuitTest.kt index 41413b743..3e3caad62 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteAndQuitTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteAndQuitTest.kt @@ -16,7 +16,6 @@ import kotlinx.coroutines.test.runBlockingTest import mozilla.components.browser.storage.sync.PlacesHistoryStorage import mozilla.components.concept.engine.Engine import mozilla.components.feature.tabs.TabsUseCases -import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.rule.MainCoroutineRule import org.junit.Before import org.junit.Ignore @@ -26,7 +25,6 @@ import org.junit.runner.RunWith import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.PermissionStorage -import org.mozilla.fenix.ext.clearAndCommit import org.mozilla.fenix.ext.components import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.utils.Settings @@ -38,8 +36,8 @@ class DeleteAndQuitTest { @get:Rule val coroutinesTestRule = MainCoroutineRule(TestCoroutineDispatcher()) - private var activity: HomeActivity = mockk(relaxed = true) - lateinit var settings: Settings + private val activity: HomeActivity = mockk(relaxed = true) + private val settings: Settings = mockk(relaxed = true) private val tabUseCases: TabsUseCases = mockk(relaxed = true) private val historyStorage: PlacesHistoryStorage = mockk(relaxed = true) private val permissionStorage: PermissionStorage = mockk(relaxed = true) @@ -49,25 +47,18 @@ class DeleteAndQuitTest { @Before fun setUp() { - settings = Settings.getInstance(testContext).apply { - clear() - } - every { activity.components.core.historyStorage } returns historyStorage every { activity.components.core.permissionStorage } returns permissionStorage every { activity.components.useCases.tabsUseCases } returns tabUseCases every { tabUseCases.removeAllTabs } returns removeAllTabsUseCases every { activity.components.core.engine } returns engine - } - - private fun Settings.clear() { - preferences.clearAndCommit() + every { activity.components.settings } returns settings } @Test fun `delete only tabs and quit`() = runBlockingTest { // When - settings.setDeleteDataOnQuit(DeleteBrowsingDataOnQuitType.TABS, true) + every { settings.getDeleteDataOnQuit(DeleteBrowsingDataOnQuitType.TABS) } returns true deleteAndQuit(activity, this, snackbar) @@ -97,7 +88,7 @@ class DeleteAndQuitTest { fun `delete everything and quit`() = runBlockingTest { // When DeleteBrowsingDataOnQuitType.values().forEach { - settings.setDeleteDataOnQuit(it, true) + every { settings.getDeleteDataOnQuit(it) } returns true } deleteAndQuit(activity, this, snackbar) diff --git a/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt b/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt index 789a58954..6bb0b26b5 100644 --- a/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt +++ b/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt @@ -4,7 +4,6 @@ package org.mozilla.fenix.utils -import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import mozilla.components.feature.sitepermissions.SitePermissionsRules import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.ALLOWED import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action.ASK_TO_ALLOW @@ -17,8 +16,7 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mozilla.fenix.ext.clearAndCommit -import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.deletebrowsingdata.DeleteBrowsingDataOnQuitType @@ -27,9 +25,18 @@ class SettingsTest { lateinit var settings: Settings + private val defaultPermissions = SitePermissionsRules( + camera = ASK_TO_ALLOW, + location = ASK_TO_ALLOW, + microphone = ASK_TO_ALLOW, + notification = ASK_TO_ALLOW, + autoplayAudible = AutoplayAction.BLOCKED, + autoplayInaudible = AutoplayAction.BLOCKED + ) + @Before fun setUp() { - settings = testContext.settings().apply(Settings::clear) + settings = Settings(testContext) } @Test @@ -103,28 +110,6 @@ class SettingsTest { assertEquals("Mozilla", settings.defaultSearchEngineName) } - @Test - fun isCrashReportingEnabled_enabledInBuild() { - // When - clearExistingInstance() - val settings = testContext.settings(true) - .apply(Settings::clear) - - // Then - assertTrue(settings.isCrashReportingEnabled) - } - - @Test - fun isCrashReportingEnabled_disabledInBuild() { - // When - clearExistingInstance() - val settings = testContext.settings(false) - .apply(Settings::clear) - - // Then - assertFalse(settings.isCrashReportingEnabled) - } - @Test fun isRemoteDebuggingEnabled() { // When just created @@ -451,7 +436,7 @@ class SettingsTest { // When just created // Then assertEquals( - defaultPermissions(), + defaultPermissions, settings.getSitePermissionsCustomSettingsRules() ) } @@ -463,7 +448,7 @@ class SettingsTest { // Then assertEquals( - defaultPermissions().copy(camera = BLOCKED), + defaultPermissions.copy(camera = BLOCKED), settings.getSitePermissionsCustomSettingsRules() ) } @@ -475,7 +460,7 @@ class SettingsTest { // Then assertEquals( - defaultPermissions().copy(notification = BLOCKED), + defaultPermissions.copy(notification = BLOCKED), settings.getSitePermissionsCustomSettingsRules() ) } @@ -487,7 +472,7 @@ class SettingsTest { // Then assertEquals( - defaultPermissions().copy(location = BLOCKED), + defaultPermissions.copy(location = BLOCKED), settings.getSitePermissionsCustomSettingsRules() ) } @@ -499,7 +484,7 @@ class SettingsTest { // Then assertEquals( - defaultPermissions().copy(microphone = BLOCKED), + defaultPermissions.copy(microphone = BLOCKED), settings.getSitePermissionsCustomSettingsRules() ) } @@ -509,7 +494,7 @@ class SettingsTest { settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.AUTOPLAY_AUDIBLE, ALLOWED) assertEquals( - defaultPermissions().copy(autoplayAudible = ALLOWED), + defaultPermissions.copy(autoplayAudible = ALLOWED), settings.getSitePermissionsCustomSettingsRules() ) } @@ -519,25 +504,8 @@ class SettingsTest { settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.AUTOPLAY_INAUDIBLE, ALLOWED) assertEquals( - defaultPermissions().copy(autoplayInaudible = ALLOWED), + defaultPermissions.copy(autoplayInaudible = ALLOWED), settings.getSitePermissionsCustomSettingsRules() ) } } - -private fun clearExistingInstance() { - Settings.instance = null -} - -private fun Settings.clear() { - preferences.clearAndCommit() -} - -private fun defaultPermissions() = SitePermissionsRules( - camera = ASK_TO_ALLOW, - location = ASK_TO_ALLOW, - microphone = ASK_TO_ALLOW, - notification = ASK_TO_ALLOW, - autoplayAudible = AutoplayAction.BLOCKED, - autoplayInaudible = AutoplayAction.BLOCKED -) diff --git a/app/src/test/java/org/mozilla/fenix/whatsnew/WhatsNewStorageTest.kt b/app/src/test/java/org/mozilla/fenix/whatsnew/WhatsNewStorageTest.kt index b8ae92778..59c2794af 100644 --- a/app/src/test/java/org/mozilla/fenix/whatsnew/WhatsNewStorageTest.kt +++ b/app/src/test/java/org/mozilla/fenix/whatsnew/WhatsNewStorageTest.kt @@ -4,25 +4,24 @@ package org.mozilla.fenix.whatsnew -import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import androidx.preference.PreferenceManager import mozilla.components.support.test.robolectric.testContext import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.ext.clearAndCommit -import org.mozilla.fenix.utils.Settings +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner @RunWith(FenixRobolectricTestRunner::class) class WhatsNewStorageTest { + private lateinit var storage: SharedPreferenceWhatsNewStorage - private lateinit var settings: Settings @Before fun setUp() { storage = SharedPreferenceWhatsNewStorage(testContext) - settings = Settings.getInstance(testContext) - .apply(Settings::clear) + PreferenceManager.getDefaultSharedPreferences(testContext).clearAndCommit() } @Test @@ -57,7 +56,3 @@ class WhatsNewStorageTest { const val DAY_IN_MILLIS = 3600 * 1000 * 24 } } - -fun Settings.clear() { - preferences.clearAndCommit() -} diff --git a/app/src/test/java/org/mozilla/fenix/whatsnew/WhatsNewTest.kt b/app/src/test/java/org/mozilla/fenix/whatsnew/WhatsNewTest.kt index 15b652833..dc16c7a34 100644 --- a/app/src/test/java/org/mozilla/fenix/whatsnew/WhatsNewTest.kt +++ b/app/src/test/java/org/mozilla/fenix/whatsnew/WhatsNewTest.kt @@ -4,24 +4,24 @@ package org.mozilla.fenix.whatsnew * 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/. */ -import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import androidx.preference.PreferenceManager import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.utils.Settings +import org.mozilla.fenix.ext.clearAndCommit +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner @RunWith(FenixRobolectricTestRunner::class) class WhatsNewTest { + private lateinit var storage: SharedPreferenceWhatsNewStorage - private lateinit var settings: Settings @Before fun setup() { storage = SharedPreferenceWhatsNewStorage(testContext) - settings = testContext.settings().apply(Settings::clear) + PreferenceManager.getDefaultSharedPreferences(testContext).clearAndCommit() WhatsNew.wasUpdatedRecently = null } From 96121a79fabdf66662aab1e886603b13aa6c7881 Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Tue, 21 Jul 2020 12:21:02 +0200 Subject: [PATCH 28/31] Issue #7877: Add SearchActionProvider for faster responses that mirror entered text. --- .../search/awesomebar/AwesomeBarInteractor.kt | 53 ++++++++ .../fenix/search/awesomebar/AwesomeBarView.kt | 125 ++++++++---------- 2 files changed, 106 insertions(+), 72 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarInteractor.kt diff --git a/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarInteractor.kt b/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarInteractor.kt new file mode 100644 index 000000000..e0dac77d0 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarInteractor.kt @@ -0,0 +1,53 @@ +/* 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.search.awesomebar + +import mozilla.components.browser.search.SearchEngine +import mozilla.components.browser.session.Session + +/** + * Interface for the AwesomeBarView Interactor. This interface is implemented by objects that want + * to respond to user interaction on the AwesomebarView + */ +interface AwesomeBarInteractor { + + /** + * Called whenever a suggestion containing a URL is tapped + * @param url the url the suggestion was providing + */ + fun onUrlTapped(url: String) + + /** + * Called whenever a search engine suggestion is tapped + * @param searchTerms the query contained by the search suggestion + */ + fun onSearchTermsTapped(searchTerms: String) + + /** + * Called whenever a search engine shortcut is tapped + * @param searchEngine the searchEngine that was selected + */ + fun onSearchShortcutEngineSelected(searchEngine: SearchEngine) + + /** + * Called whenever the "Search Engine Settings" item is tapped + */ + fun onClickSearchEngineSettings() + + /** + * Called whenever an existing session is selected from the sessionSuggestionProvider + */ + fun onExistingSessionSelected(session: Session) + + /** + * Called whenever an existing session is selected from the sessionSuggestionProvider + */ + fun onExistingSessionSelected(tabId: String) + + /** + * Called whenever the Shortcuts button is clicked + */ + fun onSearchShortcutsButtonClicked() +} diff --git a/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt b/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt index fa6328fab..3cfc8f25b 100644 --- a/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt +++ b/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt @@ -16,6 +16,7 @@ import mozilla.components.concept.awesomebar.AwesomeBar import mozilla.components.concept.engine.EngineSession import mozilla.components.feature.awesomebar.provider.BookmarksStorageSuggestionProvider import mozilla.components.feature.awesomebar.provider.HistoryStorageSuggestionProvider +import mozilla.components.feature.awesomebar.provider.SearchActionProvider import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider import mozilla.components.feature.awesomebar.provider.SessionSuggestionProvider import mozilla.components.feature.search.SearchUseCases @@ -29,51 +30,6 @@ import org.mozilla.fenix.ext.components import org.mozilla.fenix.search.SearchEngineSource import org.mozilla.fenix.search.SearchFragmentState -/** - * Interface for the AwesomeBarView Interactor. This interface is implemented by objects that want - * to respond to user interaction on the AwesomebarView - */ -interface AwesomeBarInteractor { - - /** - * Called whenever a suggestion containing a URL is tapped - * @param url the url the suggestion was providing - */ - fun onUrlTapped(url: String) - - /** - * Called whenever a search engine suggestion is tapped - * @param searchTerms the query contained by the search suggestion - */ - fun onSearchTermsTapped(searchTerms: String) - - /** - * Called whenever a search engine shortcut is tapped - * @param searchEngine the searchEngine that was selected - */ - fun onSearchShortcutEngineSelected(searchEngine: SearchEngine) - - /** - * Called whenever the "Search Engine Settings" item is tapped - */ - fun onClickSearchEngineSettings() - - /** - * Called whenever an existing session is selected from the sessionSuggestionProvider - */ - fun onExistingSessionSelected(session: Session) - - /** - * Called whenever an existing session is selected from the sessionSuggestionProvider - */ - fun onExistingSessionSelected(tabId: String) - - /** - * Called whenever the Shortcuts button is clicked - */ - fun onSearchShortcutsButtonClicked() -} - /** * View that contains and configures the BrowserAwesomeBar */ @@ -88,7 +44,8 @@ class AwesomeBarView( private val shortcutsEnginePickerProvider: ShortcutsSuggestionProvider private val bookmarksStorageSuggestionProvider: BookmarksStorageSuggestionProvider private val defaultSearchSuggestionProvider: SearchSuggestionProvider - private val searchSuggestionProviderMap: MutableMap + private val defaultSearchActionProvider: SearchActionProvider + private val searchSuggestionProviderMap: MutableMap> private var providersInUse = mutableSetOf() private val loadUrlUseCase = object : SessionUseCases.LoadUrlUseCase { @@ -141,7 +98,8 @@ class AwesomeBarView( val draw = getDrawable(context, R.drawable.ic_link)!! draw.colorFilter = createBlendModeColorFilterCompat(primaryTextColor, SRC_IN) - val engineForSpeculativeConnects = if (!isBrowsingModePrivate()) components.core.engine else null + val engineForSpeculativeConnects = + if (!isBrowsingModePrivate()) components.core.engine else null sessionProvider = SessionSuggestionProvider( context.resources, @@ -167,8 +125,9 @@ class AwesomeBarView( engineForSpeculativeConnects ) - val searchDrawable = getDrawable(context, R.drawable.ic_search)!! - searchDrawable.colorFilter = createBlendModeColorFilterCompat(primaryTextColor, SRC_IN) + val searchBitmap = getDrawable(context, R.drawable.ic_search)!!.apply { + colorFilter = createBlendModeColorFilterCompat(primaryTextColor, SRC_IN) + }.toBitmap() defaultSearchSuggestionProvider = SearchSuggestionProvider( @@ -178,9 +137,20 @@ class AwesomeBarView( fetchClient = components.core.client, mode = SearchSuggestionProvider.Mode.MULTIPLE_SUGGESTIONS, limit = 3, - icon = searchDrawable.toBitmap(), + icon = searchBitmap, showDescription = false, - engine = engineForSpeculativeConnects + engine = engineForSpeculativeConnects, + filterExactMatch = true + ) + + defaultSearchActionProvider = + SearchActionProvider( + searchEngineGetter = suspend { + components.search.searchEngineManager.getDefaultSearchEngineAsync(context) + }, + searchUseCase = searchUseCase, + icon = searchBitmap, + showDescription = false ) shortcutsEnginePickerProvider = @@ -248,9 +218,7 @@ class AwesomeBarView( } if (state.showSearchSuggestions) { - getSelectedSearchSuggestionProvider(state)?.let { - providersToAdd.add(it) - } + providersToAdd.addAll(getSelectedSearchSuggestionProvider(state)) } if (!isBrowsingModePrivate()) { @@ -274,9 +242,7 @@ class AwesomeBarView( } if (!state.showSearchSuggestions) { - getSelectedSearchSuggestionProvider(state)?.let { - providersToRemove.add(it) - } + providersToRemove.addAll(getSelectedSearchSuggestionProvider(state)) } if (isBrowsingModePrivate()) { @@ -291,9 +257,12 @@ class AwesomeBarView( ?: false } - private fun getSelectedSearchSuggestionProvider(state: SearchFragmentState): SearchSuggestionProvider? { + private fun getSelectedSearchSuggestionProvider(state: SearchFragmentState): List { return when (state.searchEngineSource) { - is SearchEngineSource.Default -> defaultSearchSuggestionProvider + is SearchEngineSource.Default -> listOf( + defaultSearchActionProvider, + defaultSearchSuggestionProvider + ) is SearchEngineSource.Shortcut -> getSuggestionProviderForEngine( state.searchEngineSource.searchEngine ) @@ -307,26 +276,38 @@ class AwesomeBarView( view.addProviders(shortcutsEnginePickerProvider) } - private fun getSuggestionProviderForEngine(engine: SearchEngine): SearchSuggestionProvider? { + private fun getSuggestionProviderForEngine(engine: SearchEngine): List { return searchSuggestionProviderMap.getOrPut(engine) { val context = container.context val components = context.components val primaryTextColor = context.getColorFromAttr(R.attr.primaryText) - val draw = getDrawable(context, R.drawable.ic_search) - draw?.colorFilter = createBlendModeColorFilterCompat(primaryTextColor, SRC_IN) + val searchBitmap = getDrawable(context, R.drawable.ic_search)?.apply { + colorFilter = createBlendModeColorFilterCompat(primaryTextColor, SRC_IN) + }?.toBitmap() - val engineForSpeculativeConnects = if (!isBrowsingModePrivate()) components.core.engine else null - - SearchSuggestionProvider( + val engineForSpeculativeConnects = + if (!isBrowsingModePrivate()) components.core.engine else null + val searchEngine = components.search.provider.installedSearchEngines(context).list.find { it.name == engine.name } - ?: components.search.provider.getDefaultEngine(context), - shortcutSearchUseCase, - components.core.client, - limit = 3, - mode = SearchSuggestionProvider.Mode.MULTIPLE_SUGGESTIONS, - icon = draw?.toBitmap(), - engine = engineForSpeculativeConnects + ?: components.search.provider.getDefaultEngine(context) + + listOf( + SearchActionProvider( + searchEngineGetter = suspend { searchEngine }, + searchUseCase = shortcutSearchUseCase, + icon = searchBitmap + ), + SearchSuggestionProvider( + searchEngine, + shortcutSearchUseCase, + components.core.client, + limit = 3, + mode = SearchSuggestionProvider.Mode.MULTIPLE_SUGGESTIONS, + icon = searchBitmap, + engine = engineForSpeculativeConnects, + filterExactMatch = true + ) ) } } From b483067bd9e81283a2e1b12fede9d1a0f0106ca8 Mon Sep 17 00:00:00 2001 From: Sawyer Blatz Date: Tue, 21 Jul 2020 10:05:27 -0700 Subject: [PATCH 29/31] For #12461: Update content description for tabs tray open tab count --- .../java/org/mozilla/fenix/tabtray/TabTrayView.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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 7177b2fad..a5e87a297 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt @@ -16,6 +16,7 @@ import androidx.lifecycle.LifecycleCoroutineScope import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.tabs.TabLayout import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.component_tabstray.* import kotlinx.android.synthetic.main.component_tabstray.view.* import kotlinx.android.synthetic.main.component_tabstray_fab.view.* import kotlinx.android.synthetic.main.tabs_tray_tab_counter.* @@ -37,7 +38,7 @@ import org.mozilla.fenix.ext.settings /** * View that contains and configures the BrowserAwesomeBar */ -@Suppress("LongParameterList") +@Suppress("LongParameterList", "TooManyFunctions", "LargeClass") class TabTrayView( private val container: ViewGroup, private val interactor: TabTrayInteractor, @@ -248,6 +249,15 @@ class TabTrayView( view.tab_tray_overflow.isVisible = !hasNoTabs counter_text.text = "${state.normalTabs.size}" + updateTabCounterContentDescription(state.normalTabs.size) + } + } + + private fun updateTabCounterContentDescription(count: Int) { + view.tab_layout.getTabAt(0)?.contentDescription = if (count == 1) { + view.context?.getString(R.string.open_tab_tray_single) + } else { + view.context?.getString(R.string.open_tab_tray_plural, count.toString()) } } From 99354174a9d7492893c9990c626a54649b46d17f Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Tue, 21 Jul 2020 15:39:28 -0400 Subject: [PATCH 30/31] For #12797: Use measured height/width for loading a thumbnail Using `onNextLayout` has the side-effect of showing the thumbnail with the tab.id from a recycled view. The root cause of needing that call is to retrieve the measured height/width from the view first before requesting. A simpler solution to avoid the complexity is to retrieve these values from the dimen resources. Co-authored-by: Gabriel Luong --- .../mozilla/fenix/tabtray/TabTrayViewHolder.kt | 18 ++++++++++++------ app/src/main/res/layout/tab_tray_item.xml | 4 ++-- app/src/main/res/values/dimens.xml | 2 ++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt index af728d664..a94b08edd 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt @@ -7,11 +7,11 @@ package org.mozilla.fenix.tabtray import android.view.View import android.view.accessibility.AccessibilityNodeInfo import android.widget.ImageButton +import android.widget.ImageView import android.widget.TextView import androidx.annotation.VisibleForTesting import androidx.appcompat.widget.AppCompatImageButton import androidx.core.content.ContextCompat -import androidx.core.view.doOnNextLayout import mozilla.components.browser.state.state.MediaState import mozilla.components.browser.tabstray.TabViewHolder import mozilla.components.browser.tabstray.thumbnail.TabThumbnailView @@ -21,7 +21,7 @@ import mozilla.components.concept.tabstray.TabsTray import mozilla.components.feature.media.ext.pauseIfPlaying import mozilla.components.feature.media.ext.playIfPaused import mozilla.components.support.base.observer.Observable -import mozilla.components.support.images.ext.loadIntoView +import mozilla.components.support.images.ImageLoadRequest import mozilla.components.support.images.loader.ImageLoader import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl import org.mozilla.fenix.R @@ -32,6 +32,7 @@ import org.mozilla.fenix.ext.removeAndDisable import org.mozilla.fenix.ext.removeTouchDelegate import org.mozilla.fenix.ext.showAndEnable import org.mozilla.fenix.ext.toTab +import kotlin.math.max /** * A RecyclerView ViewHolder implementation for "tab" items. @@ -73,10 +74,7 @@ class TabTrayViewHolder( if (tab.thumbnail != null) { thumbnailView.setImageBitmap(tab.thumbnail) } else { - // Make sure we have the view's dimensions so we can load the image at the correct size - thumbnailView.doOnNextLayout { - imageLoader.loadIntoView(thumbnailView, tab.id) - } + loadIntoThumbnailView(thumbnailView, tab.id) } // Media state @@ -182,6 +180,14 @@ class TabTrayViewHolder( closeView.context.getString(R.string.close_tab_title, title) } + private fun loadIntoThumbnailView(thumbnailView: ImageView, id: String) { + val thumbnailSize = max( + itemView.resources.getDimensionPixelSize(R.dimen.tab_tray_thumbnail_height), + itemView.resources.getDimensionPixelSize(R.dimen.tab_tray_thumbnail_width) + ) + imageLoader.loadIntoView(thumbnailView, ImageLoadRequest(id, thumbnailSize)) + } + internal fun updateAccessibilityRowIndex(item: View, newIndex: Int) { item.setAccessibilityDelegate(object : View.AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo( diff --git a/app/src/main/res/layout/tab_tray_item.xml b/app/src/main/res/layout/tab_tray_item.xml index 0a465794e..1bcd76021 100644 --- a/app/src/main/res/layout/tab_tray_item.xml +++ b/app/src/main/res/layout/tab_tray_item.xml @@ -25,8 +25,8 @@ 40dp + 92dp + 69dp 10dp From 11e2137fab347304421fcdbd0234fa93673517d5 Mon Sep 17 00:00:00 2001 From: Jeff Boek Date: Tue, 21 Jul 2020 14:34:07 -0700 Subject: [PATCH 31/31] For #12806 - Adds unit tests to cover `ToolbarView.update()` (#12807) --- .../fenix/search/toolbar/ToolbarView.kt | 4 +- .../fenix/search/toolbar/ToolbarViewTest.kt | 90 ++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/search/toolbar/ToolbarView.kt b/app/src/main/java/org/mozilla/fenix/search/toolbar/ToolbarView.kt index 3efd85f70..06efcafb1 100644 --- a/app/src/main/java/org/mozilla/fenix/search/toolbar/ToolbarView.kt +++ b/app/src/main/java/org/mozilla/fenix/search/toolbar/ToolbarView.kt @@ -7,6 +7,7 @@ package org.mozilla.fenix.search.toolbar import android.content.Context import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable +import androidx.annotation.VisibleForTesting import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider @@ -56,7 +57,8 @@ class ToolbarView( engine: Engine ) { - private var isInitialized = false + @VisibleForTesting + internal var isInitialized = false init { view.apply { diff --git a/app/src/test/java/org/mozilla/fenix/search/toolbar/ToolbarViewTest.kt b/app/src/test/java/org/mozilla/fenix/search/toolbar/ToolbarViewTest.kt index 7506fdb60..3f8c9c449 100644 --- a/app/src/test/java/org/mozilla/fenix/search/toolbar/ToolbarViewTest.kt +++ b/app/src/test/java/org/mozilla/fenix/search/toolbar/ToolbarViewTest.kt @@ -6,33 +6,58 @@ package org.mozilla.fenix.search.toolbar import android.content.Context import androidx.appcompat.view.ContextThemeWrapper +import androidx.core.graphics.drawable.toBitmap import io.mockk.MockKAnnotations import io.mockk.Runs import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just +import io.mockk.mockk import io.mockk.slot import io.mockk.spyk import io.mockk.verify import mozilla.components.browser.toolbar.BrowserToolbar +import mozilla.components.browser.toolbar.edit.EditToolbar import mozilla.components.concept.engine.Engine import mozilla.components.concept.toolbar.Toolbar import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.R +import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.search.SearchEngineSource +import org.mozilla.fenix.search.SearchFragmentState @RunWith(FenixRobolectricTestRunner::class) class ToolbarViewTest { - @MockK(relaxed = true) private lateinit var interactor: ToolbarInteractor @MockK private lateinit var engine: Engine private lateinit var context: Context private lateinit var toolbar: BrowserToolbar + private val defaultState: SearchFragmentState = SearchFragmentState( + tabId = null, + url = "", + searchTerms = "", + query = "", + searchEngineSource = SearchEngineSource.Default(mockk { + every { name } returns "Search Engine" + every { icon } returns testContext.getDrawable(R.drawable.ic_search)!!.toBitmap() + }), + defaultEngineSource = mockk(relaxed = true), + showSearchSuggestionsHint = false, + showSearchSuggestions = false, + showSearchShortcuts = false, + areShortcutsAvailable = true, + showClipboardSuggestions = false, + showHistorySuggestions = false, + showBookmarkSuggestions = false, + searchAccessPoint = Event.PerformedSearch.SearchAccessPoint.NONE + ) @Before fun setup() { @@ -72,6 +97,69 @@ class ToolbarViewTest { assertTrue(toolbar.private) } + @Test + fun `View gets initialized only once`() { + val view = buildToolbarView(false) + assertFalse(view.isInitialized) + + view.update(defaultState) + view.update(defaultState) + view.update(defaultState) + + verify(exactly = 1) { toolbar.url = any() } + verify(exactly = 1) { toolbar.setSearchTerms(any()) } + verify(exactly = 1) { interactor.onTextChanged(any()) } + // editMode gets called when the view is initialized. So it is called twice in this test + verify(exactly = 2) { toolbar.editMode() } + + assertTrue(view.isInitialized) + } + + @Test + fun `URL gets set to the states query`() { + val toolbarView = buildToolbarView(false) + toolbarView.update(defaultState.copy(query = "Query")) + + assertEquals("Query", toolbarView.view.url) + } + + @Test + fun `URL gets set to the states pastedText if exists`() { + val toolbarView = buildToolbarView(false) + toolbarView.update(defaultState.copy(query = "Query", pastedText = "Pasted")) + + assertEquals("Pasted", toolbarView.view.url) + } + + @Test + fun `searchTerms get set if pastedText is null or empty`() { + val toolbarView = buildToolbarView(false) + toolbarView.update(defaultState.copy(query = "Query", pastedText = "", searchTerms = "Search Terms")) + + verify { toolbar.setSearchTerms("Search Terms") } + } + + @Test + fun `searchTerms don't get set if pastedText has a value`() { + val toolbarView = buildToolbarView(false) + toolbarView.update( + defaultState.copy(query = "Query", pastedText = "PastedText", searchTerms = "Search Terms") + ) + + verify(exactly = 0) { toolbar.setSearchTerms("Search Terms") } + } + + @Test + fun `searchEngine name and icon get set on update`() { + val editToolbar: EditToolbar = mockk(relaxed = true) + every { toolbar.edit } returns editToolbar + + val toolbarView = buildToolbarView(false) + toolbarView.update(defaultState) + + verify { editToolbar.setIcon(any(), "Search Engine") } + } + private fun buildToolbarView(isPrivate: Boolean) = ToolbarView( context, interactor,