diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 79e5c2939..58b4bf9b0 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -23,3 +23,21 @@
/automation/ @mozilla-mobile/releng @mozilla-mobile/fenix
/taskcluster/ /@mozilla-mobile/releng @mozilla-mobile/fenix
/.github/ @mozilla-mobile/releng @mozilla-mobile/fenix
+
+# --- PERFORMANCE START --- #
+# The performance team would like to monitor some files to understand
+# when performance-impacting changes occur. Our intent is not to block
+# these changes (for now) but to be aware of them. Please let us know
+# if the CODEOWNERS system makes this impractical. We're available at
+# #perf-android-frontend on Matrix.
+/app/src/*/java/org/mozilla/fenix/perf/** @mozilla-mobile/Performance
+*.pro @mozilla-mobile/Performance
+*proguard* @mozilla-mobile/Performance
+
+# Possible startup regressions
+*Application.kt @mozilla-mobile/Performance
+
+# We want to be aware of new features behind flags as well as features
+# about to be enabled.
+FeatureFlags.kt @mozilla-mobile/Performance
+# --- PERFORMANCE END --- #
diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE
index ef365cba2..35934de05 100644
--- a/.github/PULL_REQUEST_TEMPLATE
+++ b/.github/PULL_REQUEST_TEMPLATE
@@ -6,11 +6,8 @@
- [ ] **Screenshots**: This PR includes screenshots or GIFs of the changes made or an explanation of why it does not
- [ ] **Accessibility**: The code in this PR follows [accessibility best practices](https://github.com/mozilla-mobile/shared-docs/blob/master/android/accessibility_guide.md) or does not include any user facing features. In addition, it includes a screenshot of a successful [accessibility scan](https://play.google.com/store/apps/details?id=com.google.android.apps.accessibility.auditor&hl=en_US) to ensure no new defects are added to the product.
-### After merge
-- [ ] **Milestone**: Make sure issues finished by this pull request are added to the [milestone](https://github.com/mozilla-mobile/fenix/milestones) of the version currently in development.
-
### To download an APK when reviewing a PR:
1. click on Show All Checks,
2. click Details next to "Taskcluster (pull_request)" after it appears and then finishes with a green checkmark,
3. click on the "Fenix - assemble" task, then click "Run Artifacts".
-4. the APK links should be on the left side of the screen, named for each CPU architecture
\ No newline at end of file
+4. the APK links should be on the left side of the screen, named for each CPU architecture
diff --git a/README.md b/README.md
index b93808d9a..ef1d872d0 100644
--- a/README.md
+++ b/README.md
@@ -105,21 +105,23 @@ you want these variants to be:
#### Performance Build Variants
For accurate performance measurements, read this section!
-If you want to analyze performance during **local development** (note: there is a non-trivial performance impact - see caveats):
-- Recommendation: use a debuggable variant (see "local.properties helpers" below) with local Leanplum, Adjust, & Sentry API tokens: contact the front-end perf group for access to them
-- Rationale: There are numerous performance-impacting differences between debug and release variants so we need a release variant. To profile, we also need debuggable, which is disabled by default for release variants. If API tokens are not provided, the SDKs may change their behavior in non-trivial ways.
-- Caveats:
- - debuggable has a non-trivial & variable impact on performance but is needed to take profiles.
- - Random experiment opt-in & feature flags may impact performance (see [perf-frontend-issues#45](https://github.com/mozilla-mobile/perf-frontend-issues/issues/45) for mitigation).
- - This is slower to build than debug builds because it does additional tasks (e.g. minification) similar to other release builds
+To analyze performance during **local development** build a production variant locally (this could either be the Nightly, beta or release). Otherwise, you could also grab a pre-existing APK if you don't need to test some local changes. Then, use the Firefox profiler to profile what you need!
-If you want to run **performance tests/benchmarks** in automation or locally:
-- Recommendation: production builds. If debuggable is required, use recommendation above but note the caveat above. If your needs are not met, please contact the front-end perf group to identify a new solution.
-- Rationale: like the rationale above, we need release variants so the choice is based on the debuggable flag.
+For more information on how to use the profiler or how to use the build, refer to this [how to measure performance with the build](https://wiki.mozilla.org/Performance/How_to_get_started_on_Fenix)
-For additional context on these recommendations, see [the perf build variant analysis](https://docs.google.com/document/d/1aW-m0HYncTDDiRz_2x6EjcYkjBpL9SHhhYix13Vil30/edit#).
+If you want to run **performance tests/benchmarks** in automation or locally use a production build since it is much closer in behavior compared to what users see in the wild.
-Before you can install any release variants, **you will need to sign them:** see [Automatically signing release builds](#automatically-sign-release-builds) for details.
+Before you can install any release builds, **You will need to sign production build variants:** see [Automatically signing release builds](#automatically-sign-release-builds) for details.
+
+##### Known disabled-by-default features
+Some features are disabled by default when Fenix is built locally. This can be problematic at times for checking performance since you might want to know how your code behaves with those features.
+The known features that are disabled by default are:
+- Sentry
+- Leanplum
+- Adjust
+- Mozilla Location Services (also known as MLS)
+- Firebase Push Services
+- Telemetry (only disabled by default in debug builds)
## Pre-push hooks
To reduce review turn-around time, we'd like all pushes to run tests locally. We'd
diff --git a/app/build.gradle b/app/build.gradle
index 44fa7a80b..2e22fbaca 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -468,13 +468,10 @@ dependencies {
implementation Deps.androidx_lifecycle_viewmodel
implementation Deps.androidx_core
implementation Deps.androidx_core_ktx
- implementation Deps.androidx_dynamic_animation
implementation Deps.androidx_transition
implementation Deps.androidx_work_ktx
implementation Deps.google_material
- implementation Deps.google_flexbox
-
implementation Deps.lottie
implementation Deps.adjust
@@ -482,6 +479,9 @@ dependencies {
implementation Deps.google_ads_id // Required for the Google Advertising ID
+ implementation Deps.google_play_store // Required for in-app reviews
+ implementation Deps.google_play_core_ktx // Required for in-app reviews
+
androidTestImplementation Deps.uiautomator
// Removed pending AndroidX fixes
androidTestImplementation "tools.fastlane:screengrab:2.0.0"
diff --git a/app/lint.xml b/app/lint.xml
index cf2226aea..aa99dd31f 100644
--- a/app/lint.xml
+++ b/app/lint.xml
@@ -7,6 +7,8 @@
+
+
diff --git a/app/metrics.yaml b/app/metrics.yaml
index 230f67c14..202d3face 100644
--- a/app/metrics.yaml
+++ b/app/metrics.yaml
@@ -18,28 +18,50 @@ events:
startups, not just cold startup. Note: There is a short gap
between the time application goes into background and the time
android reports the application going into the background.
- Note: This metric does not cover the following cases:
- Case # 1 -> a). open a link(for example, gmail) with in-app
- Browser (metric report custom_tab startup) b). press home button
- c). open gmail again (which brings us back to in app browser).
- Step c will not report startup metric. Case # 2 -> a). open fenix
- b). press home button c). launch fenix through app switcher/recent
- apps. step c will not report startup type.
+ Note: This metric does not record souce when app opened from
+ task switcher: open application -> press home button -> open
+ recent tasks -> choose fenix. In this case will report
+ [source = unknown, type = hot, has_saved_instance_state = false].
extra_keys:
+ type:
+ description: |
+ the startup type for opening fenix. the application and HomeActivity
+ either needs to be created or started again. possible values are
+ `cold`, `warm`, `hot` or `error`. Error is for impossible cases.
+ Please file a bug if you see the error case.
+ app created AND HomeActivity created = cold
+ app started AND HomeActivity created = warm
+ app started AND HomeActivity started = hot
+ app created AND HomeActivity started = error
source:
description: |
The method used to open Fenix. Possible values are `app_icon`,
- `custom_tab`, `link` or `unknown`
+ `custom_tab`, `link` or `unknown`. unknown is for startup sources
+ where we can't pinpoint the cause. One UNKNOWN case is the app
+ switcher where we don't know what variables to check to ensure this
+ startup wasn't caused by something else.
+ has_saved_instance_state:
+ description: |
+ boolean value whether or not startup type has a savedInstance.
+ using savedInstance, HomeActivity's previous state can be restored.
+ This is an optional key since it is not applicable to all the cases.
+ for example, when we are doing a hot start up, we cant have a
+ savedInstanceState therefore we report only [APP_ICON, HOT] instead
+ of [APP_ICON, HOT, false].
bugs:
- https://github.com/mozilla-mobile/fenix/issues/11830
+ - https://github.com/mozilla-mobile/fenix/issues/12573
+ - https://github.com/mozilla-mobile/fenix/pull/13494
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/12114#pullrequestreview-445245341
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
+ - https://github.com/mozilla-mobile/fenix/pull/13494#pullrequestreview-474050499
data_sensitivity:
- interaction
notification_emails:
- esmyth@mozilla.com
- perf-android-fe@mozilla.com
- expires: "2020-12-01"
+ expires: "2021-06-01"
app_received_intent:
type: event
description: |
@@ -62,10 +84,11 @@ events:
- https://github.com/mozilla-mobile/fenix/issues/11830
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/11940/
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
notification_emails:
- esmyth@mozilla.com
- perf-android-fe@mozilla.com
- expires: "2020-12-01"
+ expires: "2021-06-01"
app_opened:
type: event
description: |
@@ -80,12 +103,13 @@ events:
- https://github.com/mozilla-mobile/fenix/issues/10616
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- telemetry-client-dev@mozilla.com
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
search_bar_tapped:
type: event
description: |
@@ -99,11 +123,12 @@ events:
- https://github.com/mozilla-mobile/fenix/issues/959
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
entered_url:
type: event
description: |
@@ -117,11 +142,12 @@ events:
- https://github.com/mozilla-mobile/fenix/issues/959
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
performed_search:
type: event
description: |
@@ -141,11 +167,12 @@ events:
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673
- https://github.com/mozilla-mobile/fenix/pull/1677
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
browser_menu_action:
type: event
description: |
@@ -166,11 +193,12 @@ events:
- https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708
- https://github.com/mozilla-mobile/fenix/pull/5098#issuecomment-529658996
- https://github.com/mozilla-mobile/fenix/pull/6310
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
total_uri_count:
type: counter
description: |
@@ -191,7 +219,7 @@ events:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
preference_toggled:
type: event
description: |
@@ -227,12 +255,13 @@ events:
- https://github.com/mozilla-mobile/fenix/pull/6352
- https://github.com/mozilla-mobile/fenix/pull/6601
- https://github.com/mozilla-mobile/fenix/pull/6746
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- technical
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-06-01"
whats_new_tapped:
type: event
description: |
@@ -241,11 +270,12 @@ events:
- https://github.com/mozilla-mobile/fenix/issues/5021
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/5090
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
opened_link:
type: event
description: |
@@ -253,16 +283,19 @@ events:
extra_keys:
mode:
description: |
- The mode the link was opened in. Either 'PRIVATE' or 'NORMAL'.
+ The mode the link was opened in. Either 'PRIVATE' or 'NORMAL'. N.B.:
+ this probe may be incorrectly implemented: see
+ https://github.com/mozilla-mobile/fenix/issues/14133
bugs:
- https://github.com/mozilla-mobile/fenix/issues/5737
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/5975
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
tab_counter_menu_action:
type: event
description:
@@ -278,11 +311,12 @@ events:
- https://github.com/mozilla-mobile/fenix/issues/11442
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/11533
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
onboarding:
fxa_auto_signin:
@@ -298,7 +332,7 @@ onboarding:
notification_emails:
- fenix-core@mozilla.com
- erichards@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
fxa_manual_signin:
type: event
description:
@@ -312,7 +346,7 @@ onboarding:
notification_emails:
- fenix-core@mozilla.com
- erichards@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
privacy_notice:
type: event
description:
@@ -326,7 +360,7 @@ onboarding:
notification_emails:
- fenix-core@mozilla.com
- erichards@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
pref_toggled_private_browsing:
type: event
description:
@@ -340,7 +374,7 @@ onboarding:
notification_emails:
- fenix-core@mozilla.com
- erichards@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
pref_toggled_toolbar_position:
type: event
description:
@@ -359,7 +393,7 @@ onboarding:
notification_emails:
- fenix-core@mozilla.com
- erichards@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
pref_toggled_tracking_prot:
type: event
description:
@@ -378,7 +412,7 @@ onboarding:
notification_emails:
- fenix-core@mozilla.com
- erichards@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
whats_new:
type: event
description:
@@ -392,7 +426,7 @@ onboarding:
notification_emails:
- fenix-core@mozilla.com
- erichards@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
pref_toggled_theme_picker:
type: event
description:
@@ -411,7 +445,7 @@ onboarding:
notification_emails:
- fenix-core@mozilla.com
- erichards@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
finish:
type: event
description:
@@ -425,7 +459,7 @@ onboarding:
notification_emails:
- fenix-core@mozilla.com
- erichards@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
search_shortcuts:
selected:
@@ -442,7 +476,7 @@ search_shortcuts:
- https://github.com/mozilla-mobile/fenix/pull/1202#issuecomment-476870449
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
toolbar_settings:
changed_position:
@@ -461,7 +495,7 @@ toolbar_settings:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
crash_reporter:
opened:
@@ -472,11 +506,12 @@ crash_reporter:
- https://github.com/mozilla-mobile/fenix/issues/1040
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
closed:
type: event
description: |
@@ -490,11 +525,12 @@ crash_reporter:
- https://github.com/mozilla-mobile/fenix/issues/1040
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
context_menu:
item_tapped:
@@ -514,11 +550,12 @@ context_menu:
- https://github.com/mozilla-mobile/fenix/issues/957
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
login_dialog:
displayed:
@@ -587,7 +624,7 @@ find_in_page:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
closed:
type: event
description: |
@@ -600,7 +637,7 @@ find_in_page:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
searched_page:
type: event
description: |
@@ -613,7 +650,7 @@ find_in_page:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
metrics:
default_browser:
@@ -631,7 +668,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
top_sites_count:
type: counter
lifetime: application
@@ -651,7 +688,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
has_top_sites:
type: boolean
lifetime: application
@@ -667,7 +704,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
recently_used_pwa_count:
type: counter
lifetime: application
@@ -736,7 +773,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
mozilla_products:
type: string_list
lifetime: application
@@ -757,7 +794,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
default_moz_browser:
type: string
lifetime: application
@@ -776,7 +813,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
adjust_campaign:
type: string
lifetime: application
@@ -795,7 +832,7 @@ metrics:
- technical
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
adjust_ad_group:
type: string
lifetime: application
@@ -814,7 +851,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
adjust_creative:
type: string
lifetime: application
@@ -833,7 +870,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
adjust_network:
type: string
lifetime: application
@@ -852,7 +889,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
toolbar_position:
type: string
lifetime: application
@@ -868,7 +905,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
search_widget_installed:
type: boolean
lifetime: application
@@ -884,7 +921,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
tabs_open_count:
type: counter
lifetime: application
@@ -904,7 +941,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
has_open_tabs:
type: boolean
lifetime: application
@@ -920,7 +957,7 @@ metrics:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
preferences:
show_search_suggestions:
@@ -938,7 +975,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
remote_debugging:
type: string_list
description: >
@@ -954,7 +991,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
telemetry:
type: string_list
description: >
@@ -972,7 +1009,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
tracking_protection:
type: string_list
description: >
@@ -989,7 +1026,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
search_bookmarks:
type: string_list
description: >
@@ -1005,7 +1042,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
search_browsing_history:
type: string_list
description: >
@@ -1021,7 +1058,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
show_clipboard_suggestions:
type: string_list
description: >
@@ -1037,7 +1074,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
show_search_shortcuts:
type: string_list
description: >
@@ -1053,7 +1090,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
open_links_in_a_private_tab:
type: string_list
description: >
@@ -1069,7 +1106,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
sync:
type: string_list
description: >
@@ -1085,7 +1122,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
sync_items:
type: string_list
description: >
@@ -1103,7 +1140,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
show_voice_search:
type: string_list
description: >
@@ -1119,7 +1156,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
search_suggestions_private:
type: string_list
description: >
@@ -1136,7 +1173,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
toolbar_position:
type: string_list
description: >
@@ -1152,7 +1189,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
accessibility_services:
type: string_list
description: >
@@ -1169,7 +1206,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
open_links_in_app:
type: string_list
description: >
@@ -1185,7 +1222,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
theme:
type: string_list
description: >
@@ -1201,7 +1238,7 @@ preferences:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
search.default_engine:
code:
@@ -1224,7 +1261,7 @@ search.default_engine:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
name:
type: string
lifetime: application
@@ -1245,7 +1282,7 @@ search.default_engine:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
submission_url:
type: string
lifetime: application
@@ -1267,7 +1304,7 @@ search.default_engine:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
bookmarks_management:
open_in_new_tab:
@@ -1278,11 +1315,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/974
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
open_in_new_tabs:
type: event
description: |
@@ -1291,11 +1329,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/974
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
open_in_private_tab:
type: event
description: |
@@ -1304,11 +1343,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/974
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
open_in_private_tabs:
type: event
description: |
@@ -1317,11 +1357,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/974
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
edited:
type: event
description: |
@@ -1330,11 +1371,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/974
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
moved:
type: event
description: |
@@ -1343,11 +1385,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/974
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
removed:
type: event
description: |
@@ -1356,11 +1399,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/974
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
multi_removed:
type: event
description: |
@@ -1369,11 +1413,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/974
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
shared:
type: event
description: |
@@ -1382,11 +1427,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/974
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
copied:
type: event
description: |
@@ -1395,11 +1441,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/974
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
folder_add:
type: event
description: |
@@ -1408,11 +1455,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/974
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1708
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
folder_remove:
type: event
description: |
@@ -1421,11 +1469,12 @@ bookmarks_management:
- https://github.com/mozilla-mobile/fenix/issues/3174
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/3724
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
custom_tab:
closed:
@@ -1436,11 +1485,12 @@ custom_tab:
- https://github.com/mozilla-mobile/fenix/issues/977
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1697
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
action_button:
type: event
description: |
@@ -1449,11 +1499,12 @@ custom_tab:
- https://github.com/mozilla-mobile/fenix/issues/977
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1697
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
menu:
type: event
description: |
@@ -1462,11 +1513,12 @@ custom_tab:
- https://github.com/mozilla-mobile/fenix/issues/977
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1697
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
activation:
identifier:
@@ -1482,11 +1534,12 @@ activation:
- https://bugzilla.mozilla.org/1501822
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1707#issuecomment-486972209
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- highly_sensitive
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
activation_id:
type: uuid
lifetime: user
@@ -1499,11 +1552,12 @@ activation:
- https://bugzilla.mozilla.org/1538011
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/1707#issuecomment-486972209
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- highly_sensitive
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
no_lint:
- USER_LIFETIME_EXPIRATION
@@ -1520,7 +1574,7 @@ qr_scanner:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
prompt_displayed:
type: event
description: |
@@ -1534,7 +1588,7 @@ qr_scanner:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
navigation_allowed:
type: event
description: |
@@ -1548,7 +1602,7 @@ qr_scanner:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
navigation_denied:
type: event
description: |
@@ -1562,7 +1616,7 @@ qr_scanner:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
error_page:
visited_error:
@@ -1576,11 +1630,12 @@ error_page:
- https://github.com/mozilla-mobile/fenix/issues/1242
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/2491#issuecomment-492414486
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
sync_auth:
opened:
@@ -1595,7 +1650,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
closed:
type: event
description: |
@@ -1608,7 +1663,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
use_email:
type: event
description: |
@@ -1622,7 +1677,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
use_email_problem:
type: event
description: |
@@ -1635,7 +1690,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
sign_in:
type: event
description: |
@@ -1649,7 +1704,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
sign_out:
type: event
description: |
@@ -1663,7 +1718,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
sign_up:
type: event
description: |
@@ -1677,7 +1732,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
paired:
type: event
description: |
@@ -1692,7 +1747,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
auto_login:
type: event
description: |
@@ -1707,7 +1762,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
recovered:
type: event
description: |
@@ -1722,7 +1777,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
other_external:
type: event
description: |
@@ -1737,7 +1792,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
scan_pairing:
type: event
description: |
@@ -1750,7 +1805,7 @@ sync_auth:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
sync_account:
opened:
@@ -1765,7 +1820,7 @@ sync_account:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
closed:
type: event
description: |
@@ -1778,7 +1833,7 @@ sync_account:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
sync_now:
type: event
description: |
@@ -1791,7 +1846,7 @@ sync_account:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
send_tab:
type: event
description: |
@@ -1804,7 +1859,7 @@ sync_account:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
sign_in_to_send_tab:
type: event
description: |
@@ -1817,7 +1872,7 @@ sync_account:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
history:
opened:
@@ -1832,7 +1887,7 @@ history:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
removed:
type: event
description: |
@@ -1845,7 +1900,7 @@ history:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
removed_all:
type: event
description: |
@@ -1858,7 +1913,7 @@ history:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
shared:
type: event
description: |
@@ -1871,7 +1926,7 @@ history:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
opened_item:
type: event
description: |
@@ -1884,7 +1939,7 @@ history:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
tip:
displayed:
@@ -1902,7 +1957,7 @@ tip:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
pressed:
type: event
description: |
@@ -1918,7 +1973,7 @@ tip:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
closed:
type: event
description: |
@@ -1934,7 +1989,7 @@ tip:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
reader_mode:
available:
@@ -1949,7 +2004,7 @@ reader_mode:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
opened:
type: event
description: |
@@ -1962,7 +2017,7 @@ reader_mode:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
closed:
type: event
description: |
@@ -1975,7 +2030,7 @@ reader_mode:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
appearance:
type: event
description: |
@@ -1988,7 +2043,7 @@ reader_mode:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
tabs_tray:
opened:
@@ -2003,7 +2058,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
closed:
type: event
description: |
@@ -2016,7 +2071,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
opened_existing_tab:
type: event
description: |
@@ -2029,7 +2084,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
closed_existing_tab:
type: event
description: |
@@ -2042,7 +2097,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
private_mode_tapped:
type: event
description: |
@@ -2055,7 +2110,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
normal_mode_tapped:
type: event
description: |
@@ -2068,7 +2123,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
new_tab_tapped:
type: event
description: |
@@ -2081,7 +2136,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
new_private_tab_tapped:
type: event
description: |
@@ -2094,7 +2149,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
menu_opened:
type: event
description: |
@@ -2107,7 +2162,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
save_to_collection:
type: event
description: |
@@ -2121,7 +2176,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
share_all_tabs:
type: event
description: |
@@ -2135,7 +2190,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
close_all_tabs:
type: event
description: |
@@ -2149,7 +2204,7 @@ tabs_tray:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
collections:
renamed:
@@ -2160,12 +2215,13 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/3935
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- technical
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
tab_restored:
type: event
description: |
@@ -2174,12 +2230,13 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/3935
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- technical
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
all_tabs_restored:
type: event
description: |
@@ -2188,12 +2245,13 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/3935
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- technical
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
tab_removed:
type: event
description: |
@@ -2202,12 +2260,13 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/3935
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- technical
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
shared:
type: event
description: |
@@ -2216,12 +2275,13 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/3935
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- technical
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
removed:
type: event
description: |
@@ -2230,12 +2290,13 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/3935
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- technical
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
saved:
type: event
description: |
@@ -2249,12 +2310,13 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/3935
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- technical
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
tabs_added:
type: event
description: |
@@ -2268,12 +2330,13 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/3935
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- technical
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
tab_select_opened:
type: event
description: |
@@ -2283,12 +2346,13 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/3935
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- technical
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
add_tab_button:
type: event
description: |
@@ -2297,11 +2361,12 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/4358
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
long_press:
type: event
description: |
@@ -2310,11 +2375,12 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/4358
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
save_button:
type: event
description: |
@@ -2325,11 +2391,12 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/4358
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
extra_keys:
from_screen:
description: |
@@ -2343,11 +2410,12 @@ collections:
- https://github.com/mozilla-mobile/fenix/issues/969
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/4539
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
search_widget:
new_tab_button:
@@ -2363,7 +2431,7 @@ search_widget:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
voice_button:
type: event
description: |
@@ -2376,7 +2444,7 @@ search_widget:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
search_widget_cfr:
displayed:
@@ -2391,7 +2459,7 @@ search_widget_cfr:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
add_widget_pressed:
type: event
description: |
@@ -2404,7 +2472,7 @@ search_widget_cfr:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
not_now_pressed:
type: event
description: |
@@ -2417,7 +2485,7 @@ search_widget_cfr:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
canceled:
type: event
description: |
@@ -2431,7 +2499,7 @@ search_widget_cfr:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
private_browsing_mode:
garbage_icon:
@@ -2447,7 +2515,7 @@ private_browsing_mode:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
snackbar_undo:
type: event
description: |
@@ -2461,7 +2529,7 @@ private_browsing_mode:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
notification_tapped:
type: event
description: |
@@ -2474,7 +2542,7 @@ private_browsing_mode:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
notification_open:
type: event
description: |
@@ -2487,7 +2555,7 @@ private_browsing_mode:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
notification_delete:
type: event
description: |
@@ -2501,7 +2569,7 @@ private_browsing_mode:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
contextual_hint.tracking_protection:
display:
@@ -2513,11 +2581,12 @@ contextual_hint.tracking_protection:
- https://github.com/mozilla-mobile/fenix/issues/9625
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/11923
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
dismiss:
type: event
description: |
@@ -2528,11 +2597,12 @@ contextual_hint.tracking_protection:
- https://github.com/mozilla-mobile/fenix/issues/9625
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/11923
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
outside_tap:
type: event
description: |
@@ -2542,11 +2612,12 @@ contextual_hint.tracking_protection:
- https://github.com/mozilla-mobile/fenix/issues/9625
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/11923
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
inside_tap:
type: event
description: |
@@ -2556,11 +2627,12 @@ contextual_hint.tracking_protection:
- https://github.com/mozilla-mobile/fenix/issues/9625
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/11923
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
tracking_protection:
exception_added:
@@ -2576,7 +2648,7 @@ tracking_protection:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
panel_settings:
type: event
description: |
@@ -2589,7 +2661,7 @@ tracking_protection:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
etp_shield:
type: event
description: |
@@ -2602,7 +2674,7 @@ tracking_protection:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
etp_tracker_list:
type: event
description: |
@@ -2616,7 +2688,7 @@ tracking_protection:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
etp_settings:
type: event
description: |
@@ -2629,7 +2701,7 @@ tracking_protection:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
etp_setting_changed:
type: event
description: |
@@ -2648,7 +2720,7 @@ tracking_protection:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
private_browsing_shortcut:
create_shortcut:
@@ -2663,7 +2735,7 @@ private_browsing_shortcut:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
cfr_add_shortcut:
type: event
description: |
@@ -2677,7 +2749,7 @@ private_browsing_shortcut:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
cfr_cancel:
type: event
description: |
@@ -2691,7 +2763,7 @@ private_browsing_shortcut:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
pinned_shortcut_priv:
type: event
description: |
@@ -2705,7 +2777,7 @@ private_browsing_shortcut:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
static_shortcut_tab:
type: event
description: |
@@ -2719,7 +2791,7 @@ private_browsing_shortcut:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
static_shortcut_priv:
type: event
description: |
@@ -2733,7 +2805,7 @@ private_browsing_shortcut:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
tab:
media_play:
@@ -2748,7 +2820,7 @@ tab:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
no_lint:
- COMMON_PREFIX
media_pause:
@@ -2763,7 +2835,7 @@ tab:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
media_notification:
play:
@@ -2778,7 +2850,7 @@ media_notification:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
pause:
type: event
description: |
@@ -2791,7 +2863,7 @@ media_notification:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
media_state:
play:
@@ -2806,7 +2878,7 @@ media_state:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
pause:
type: event
description: |
@@ -2819,7 +2891,7 @@ media_state:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
stop:
type: event
description: |
@@ -2832,7 +2904,7 @@ media_state:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
logins:
open_logins:
@@ -2847,7 +2919,7 @@ logins:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
open_individual_login:
type: event
description: |
@@ -2860,7 +2932,7 @@ logins:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
copy_login:
type: event
description: |
@@ -2873,7 +2945,7 @@ logins:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
view_password_login:
type: event
description: |
@@ -2886,7 +2958,7 @@ logins:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
save_logins_setting_changed:
type: event
description: |
@@ -2904,7 +2976,7 @@ logins:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
open_login_editor:
type: event
description: |
@@ -2917,7 +2989,7 @@ logins:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
delete_saved_login:
type: event
description: |
@@ -2930,7 +3002,7 @@ logins:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
save_edited_login:
type: event
description: |
@@ -2943,7 +3015,7 @@ logins:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
download_notification:
resume:
@@ -2954,11 +3026,12 @@ download_notification:
- https://github.com/mozilla-mobile/fenix/issues/5583
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/6554
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
pause:
type: event
description: |
@@ -2967,11 +3040,12 @@ download_notification:
- https://github.com/mozilla-mobile/fenix/issues/5583
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/6554
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
cancel:
type: event
description: |
@@ -2980,11 +3054,12 @@ download_notification:
- https://github.com/mozilla-mobile/fenix/issues/5583
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/6554
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
try_again:
type: event
description: |
@@ -2994,11 +3069,12 @@ download_notification:
- https://github.com/mozilla-mobile/fenix/issues/5583
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/6554
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
open:
type: event
description: |
@@ -3007,11 +3083,12 @@ download_notification:
- https://github.com/mozilla-mobile/fenix/issues/5583
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/6554
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
in_app_open:
type: event
description: |
@@ -3020,11 +3097,12 @@ download_notification:
- https://github.com/mozilla-mobile/fenix/issues/5583
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/6554
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
in_app_try_again:
type: event
description: |
@@ -3034,11 +3112,12 @@ download_notification:
- https://github.com/mozilla-mobile/fenix/issues/5583
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/6554
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
user_specified_search_engines:
custom_engine_added:
@@ -3053,7 +3132,7 @@ user_specified_search_engines:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
no_lint:
- COMMON_PREFIX
custom_engine_deleted:
@@ -3069,7 +3148,7 @@ user_specified_search_engines:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
search_suggestions:
enable_in_private:
@@ -3085,7 +3164,7 @@ search_suggestions:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
voice_search:
tapped:
@@ -3100,7 +3179,7 @@ voice_search:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
top_sites:
open_default:
@@ -3115,7 +3194,7 @@ top_sites:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
open_in_new_tab:
type: event
description: |
@@ -3128,7 +3207,7 @@ top_sites:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
open_in_private_tab:
type: event
description: |
@@ -3141,7 +3220,7 @@ top_sites:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
remove:
type: event
description: |
@@ -3154,7 +3233,7 @@ top_sites:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
about_page:
support_tapped:
@@ -3165,11 +3244,12 @@ about_page:
- https://github.com/mozilla-mobile/fenix/issues/6834
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8047
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
privacy_notice_tapped:
type: event
description: |
@@ -3178,50 +3258,12 @@ about_page:
- https://github.com/mozilla-mobile/fenix/issues/6834
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8047
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
- rights_tapped:
- type: event
- description: |
- A user tapped on "Know your rights" item from About page
- bugs:
- - https://github.com/mozilla-mobile/fenix/issues/6834
- data_reviews:
- - https://github.com/mozilla-mobile/fenix/pull/8047
- data_sensitivity:
- - interaction
- notification_emails:
- - fenix-core@mozilla.com
- expires: "2020-10-01"
- licensing_tapped:
- type: event
- description: |
- A user tapped on "Licensing information" item from About page
- bugs:
- - https://github.com/mozilla-mobile/fenix/issues/6834
- data_reviews:
- - https://github.com/mozilla-mobile/fenix/pull/8047
- data_sensitivity:
- - interaction
- notification_emails:
- - fenix-core@mozilla.com
- expires: "2020-10-01"
- libraries_tapped:
- type: event
- description: |
- A user tapped on "Libraries that we use" item from About page
- bugs:
- - https://github.com/mozilla-mobile/fenix/issues/6834
- data_reviews:
- - https://github.com/mozilla-mobile/fenix/pull/8047
- data_sensitivity:
- - interaction
- notification_emails:
- - fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
app_theme:
dark_theme_selected:
@@ -3237,11 +3279,12 @@ app_theme:
- https://github.com/mozilla-mobile/fenix/issues/7289
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/7968
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-04-01"
pocket:
pocket_top_site_clicked:
@@ -3256,7 +3299,7 @@ pocket:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
no_lint:
- COMMON_PREFIX
@@ -3272,7 +3315,7 @@ pocket:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
first_session:
campaign:
@@ -3290,7 +3333,7 @@ first_session:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
network:
type: string
send_in_pings:
@@ -3306,7 +3349,7 @@ first_session:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
adgroup:
type: string
send_in_pings:
@@ -3322,7 +3365,7 @@ first_session:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
creative:
send_in_pings:
- first-session
@@ -3338,7 +3381,7 @@ first_session:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
timestamp:
send_in_pings:
- first-session
@@ -3356,7 +3399,7 @@ first_session:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
browser.search:
with_ads:
@@ -3374,7 +3417,7 @@ browser.search:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
ad_clicks:
type: labeled_counter
description: |
@@ -3390,7 +3433,7 @@ browser.search:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
in_content:
type: labeled_counter
description: |
@@ -3405,7 +3448,7 @@ browser.search:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
addons:
open_addons_in_settings:
@@ -3416,11 +3459,12 @@ addons:
- https://github.com/mozilla-mobile/fenix/issues/6174
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8318
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
open_addon_in_toolbar_menu:
type: event
description: |
@@ -3433,11 +3477,12 @@ addons:
- https://github.com/mozilla-mobile/fenix/issues/6174
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8318
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
has_installed_addons:
type: boolean
description: |
@@ -3448,11 +3493,12 @@ addons:
- https://github.com/mozilla-mobile/fenix/issues/6174
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8318
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
has_enabled_addons:
type: boolean
description: |
@@ -3463,11 +3509,12 @@ addons:
- https://github.com/mozilla-mobile/fenix/issues/6174
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/8318
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
installed_addons:
type: string_list
description: |
@@ -3478,11 +3525,12 @@ addons:
- https://github.com/mozilla-mobile/fenix/issues/8920
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/11080
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
enabled_addons:
type: string_list
description: |
@@ -3493,11 +3541,12 @@ addons:
- https://github.com/mozilla-mobile/fenix/issues/8920
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/11080
+ - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
data_sensitivity:
- interaction
notification_emails:
- fenix-core@mozilla.com
- expires: "2020-10-01"
+ expires: "2021-04-01"
startup.timeline:
framework_start:
@@ -3524,7 +3573,7 @@ startup.timeline:
notification_emails:
- perf-android-fe@mozilla.com
- mcomella@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
framework_start_error:
send_in_pings:
- startup-timeline
@@ -3542,7 +3591,7 @@ startup.timeline:
notification_emails:
- perf-android-fe@mozilla.com
- mcomella@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
framework_start_read_error:
send_in_pings:
- startup-timeline
@@ -3560,7 +3609,7 @@ startup.timeline:
notification_emails:
- perf-android-fe@mozilla.com
- mcomella@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
clock_ticks_per_second:
send_in_pings:
- startup-timeline
@@ -3578,7 +3627,7 @@ startup.timeline:
notification_emails:
- perf-android-fe@mozilla.com
- mcomella@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
perf.awesomebar:
history_suggestions:
@@ -3598,7 +3647,7 @@ perf.awesomebar:
notification_emails:
- fenix-core@mozilla.com
- gkruglov@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
bookmark_suggestions:
send_in_pings:
- metrics
@@ -3616,7 +3665,7 @@ perf.awesomebar:
notification_emails:
- fenix-core@mozilla.com
- gkruglov@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
search_engine_suggestions:
send_in_pings:
- metrics
@@ -3634,7 +3683,7 @@ perf.awesomebar:
notification_emails:
- fenix-core@mozilla.com
- gkruglov@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
session_suggestions:
send_in_pings:
- metrics
@@ -3652,7 +3701,7 @@ perf.awesomebar:
notification_emails:
- fenix-core@mozilla.com
- gkruglov@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
synced_tabs_suggestions:
send_in_pings:
- metrics
@@ -3670,7 +3719,7 @@ perf.awesomebar:
notification_emails:
- fenix-core@mozilla.com
- gkruglov@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
clipboard_suggestions:
send_in_pings:
- metrics
@@ -3688,7 +3737,7 @@ perf.awesomebar:
notification_emails:
- fenix-core@mozilla.com
- gkruglov@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
shortcuts_suggestions:
send_in_pings:
- metrics
@@ -3706,7 +3755,7 @@ perf.awesomebar:
notification_emails:
- fenix-core@mozilla.com
- gkruglov@mozilla.com
- expires: "2020-10-01"
+ expires: "2020-11-15"
autoplay:
visited_setting:
diff --git a/app/src/androidTest/java/org/mozilla/fenix/helpers/HomeActivityTestRule.kt b/app/src/androidTest/java/org/mozilla/fenix/helpers/HomeActivityTestRule.kt
index 9beaf4524..2b47a32a0 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/helpers/HomeActivityTestRule.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/HomeActivityTestRule.kt
@@ -5,7 +5,9 @@
package org.mozilla.fenix.helpers
import androidx.test.espresso.intent.rule.IntentsTestRule
+import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
+import androidx.test.uiautomator.UiDevice
import org.mozilla.fenix.HomeActivity
/**
@@ -16,7 +18,12 @@ import org.mozilla.fenix.HomeActivity
*/
class HomeActivityTestRule(initialTouchMode: Boolean = false, launchActivity: Boolean = true) :
- ActivityTestRule(HomeActivity::class.java, initialTouchMode, launchActivity)
+ ActivityTestRule(HomeActivity::class.java, initialTouchMode, launchActivity) {
+ override fun beforeActivityLaunched() {
+ super.beforeActivityLaunched()
+ setLongTapTimeout()
+ }
+}
/**
* A [org.junit.Rule] to handle shared test set up for tests on [HomeActivity]. This adds
@@ -26,5 +33,19 @@ class HomeActivityTestRule(initialTouchMode: Boolean = false, launchActivity: Bo
* @param launchActivity See [IntentsTestRule]
*/
-class HomeActivityIntentTestRule(initialTouchMode: Boolean = false, launchActivity: Boolean = true) :
- IntentsTestRule(HomeActivity::class.java, initialTouchMode, launchActivity)
+class HomeActivityIntentTestRule(
+ initialTouchMode: Boolean = false,
+ launchActivity: Boolean = true
+) :
+ IntentsTestRule(HomeActivity::class.java, initialTouchMode, launchActivity) {
+ override fun beforeActivityLaunched() {
+ super.beforeActivityLaunched()
+ setLongTapTimeout()
+ }
+}
+
+// changing the device preference for Touch and Hold delay, to avoid long-clicks instead of a single-click
+fun setLongTapTimeout() {
+ val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ mDevice.executeShellCommand("settings put secure long_press_timeout 3000")
+}
diff --git a/app/src/androidTest/java/org/mozilla/fenix/helpers/RecyclerViewIdlingResource.kt b/app/src/androidTest/java/org/mozilla/fenix/helpers/RecyclerViewIdlingResource.kt
index 1908835ab..61de022cf 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/helpers/RecyclerViewIdlingResource.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/RecyclerViewIdlingResource.kt
@@ -3,13 +3,13 @@ package org.mozilla.fenix.helpers
import androidx.test.espresso.IdlingResource
import androidx.test.espresso.IdlingResource.ResourceCallback
-class RecyclerViewIdlingResource constructor(private val recycler: androidx.recyclerview.widget.RecyclerView) :
+class RecyclerViewIdlingResource constructor(private val recycler: androidx.recyclerview.widget.RecyclerView, val minItemCount: Int = 0) :
IdlingResource {
private var callback: ResourceCallback? = null
override fun isIdleNow(): Boolean {
- if (recycler.adapter != null && recycler.adapter!!.itemCount > 0) {
+ if (recycler.adapter != null && recycler.adapter!!.itemCount > minItemCount) {
if (callback != null) {
callback!!.onTransitionToIdle()
}
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 3bb2f5348..ddcdd7c84 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/BookmarksTest.kt
@@ -113,7 +113,7 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
verifyBookmarkedURL(defaultWebPage.url.toString())
@@ -121,21 +121,19 @@ class BookmarksTest {
}
}
- @Ignore("Intermittent failures: https://github.com/mozilla-mobile/fenix/issues/10911")
@Test
fun createBookmarkFolderTest() {
homeScreen {
}.openThreeDotMenu {
}.openBookmarks {
- clickAddFolderButton()
- verifyKeyboardVisible()
- addNewFolderName(bookmarksFolderName)
- saveNewFolder()
-
bookmarksListIdlingResource =
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
+ clickAddFolderButton()
+ verifyKeyboardVisible()
+ addNewFolderName(bookmarksFolderName)
+ saveNewFolder()
verifyFolderTitle(bookmarksFolderName)
verifyKeyboardHidden()
}
@@ -163,7 +161,7 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
}.openThreeDotMenu(defaultWebPage.url) {
IdlingRegistry.getInstance().unregister(bookmarksListIdlingResource!!)
@@ -193,7 +191,7 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
}.openThreeDotMenu(defaultWebPage.url) {
}.clickCopy {
@@ -210,7 +208,7 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
}.openThreeDotMenu(defaultWebPage.url) {
}.clickShare {
@@ -230,7 +228,7 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
}.openThreeDotMenu(defaultWebPage.url) {
}.clickOpenInNewTab {
@@ -249,7 +247,7 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
}.openThreeDotMenu(defaultWebPage.url) {
}.clickOpenInPrivateTab {
@@ -268,9 +266,10 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
}.openThreeDotMenu(defaultWebPage.url) {
+ IdlingRegistry.getInstance().unregister(bookmarksListIdlingResource!!)
}.clickDelete {
verifyDeleteSnackBarText()
verifyUndoDeleteSnackBarButton()
@@ -306,7 +305,7 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
longTapSelectItem(defaultWebPage.url)
@@ -336,7 +335,7 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
longTapSelectItem(defaultWebPage.url)
@@ -359,7 +358,7 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
longTapSelectItem(defaultWebPage.url)
@@ -384,11 +383,12 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
longTapSelectItem(firstWebPage.url)
longTapSelectItem(secondWebPage.url)
+ IdlingRegistry.getInstance().unregister(bookmarksListIdlingResource!!)
openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
}
@@ -410,7 +410,7 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
longTapSelectItem(defaultWebPage.url)
@@ -466,7 +466,7 @@ class BookmarksTest {
}.openThreeDotMenu {
}.openBookmarks {
bookmarksListIdlingResource =
- RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list))
+ RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1)
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
}.openThreeDotMenu(defaultWebPage.url) {
IdlingRegistry.getInstance().unregister(bookmarksListIdlingResource!!)
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/DownloadTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/DownloadTest.kt
index 45de4045a..cef6dbeb2 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/DownloadTest.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/DownloadTest.kt
@@ -21,6 +21,7 @@ import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.ui.robots.downloadRobot
import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.navigationToolbar
+import org.mozilla.fenix.ui.robots.notificationShade
import java.io.File
/**
@@ -92,10 +93,7 @@ class DownloadTest {
}
@Test
- @Ignore("Temp disable flakey test - see: https://github.com/mozilla-mobile/fenix/issues/5462")
fun testDownloadNotification() {
- homeScreen { }.dismissOnboarding()
-
val defaultWebPage = TestAssetHelper.getDownloadAsset(mockWebServer)
navigationToolbar {
@@ -108,7 +106,13 @@ class DownloadTest {
verifyDownloadPrompt()
}.clickDownload {
verifyDownloadNotificationPopup()
- verifyDownloadNotificationShade()
}
+
+ mDevice.openNotification()
+ notificationShade {
+ verifySystemNotificationExists("Download completed")
+ }
+ // close notification shade before the next test
+ mDevice.pressBack()
}
}
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 c1b65d5f2..4add929b9 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt
@@ -254,11 +254,9 @@ class HistoryTest {
navigationToolbar {
}.enterURLAndEnterToBrowser(firstWebPage.url) {
- }.openTabDrawer { }.openHomeScreen { }
-
- navigationToolbar {
- }.enterURLAndEnterToBrowser(secondWebPage.url) {
- mDevice.waitForIdle()
+ }.openTabDrawer {
+ }.openNewTab {
+ }.submitQuery(secondWebPage.url.toString()) {
}.openThreeDotMenu {
}.openHistory {
longTapSelectItem(firstWebPage.url)
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SearchTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SearchTest.kt
index a57ec57c7..11454a188 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/SearchTest.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SearchTest.kt
@@ -57,7 +57,7 @@ class SearchTest {
}.goBack {
}.goBack {
}.openSearch {
- verifySearchWithText()
+// verifySearchWithText()
clickSearchEngineButton("DuckDuckGo")
typeSearch("mozilla")
verifySearchEngineResults("DuckDuckGo")
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt
index b9963cb98..26368bd0a 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAboutTest.kt
@@ -10,7 +10,6 @@ import okhttp3.mockwebserver.MockWebServer
import org.junit.Rule
import org.junit.Before
import org.junit.After
-import org.junit.Ignore
import org.junit.Test
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
@@ -69,7 +68,7 @@ class SettingsAboutTest {
}
}
- @Ignore("Failing, see: https://github.com/mozilla-mobile/fenix/issues/13219")
+
@Test
fun verifyAboutFirefoxPreview() {
homeScreen {
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt
index ee20a2c55..4e1127875 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt
@@ -191,7 +191,8 @@ class SettingsBasicsTest {
}.enterURLAndEnterToBrowser(webpage) {
checkTextSizeOnWebsite(textSizePercentage, fenixApp.components)
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
}.openThreeDotMenu {
}.openSettings {
}.openAccessibilitySubMenu {
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt
index c146a5213..a963f5c51 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt
@@ -194,7 +194,8 @@ class SettingsPrivacyTest {
// Click save to save the login
saveLoginFromPrompt("Save")
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
}.openThreeDotMenu {
}.openSettings {
TestHelper.scrollToElementByText("Logins and passwords")
@@ -219,7 +220,8 @@ class SettingsPrivacyTest {
// Don't save the login, add to exceptions
saveLoginFromPrompt("Never save")
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
}.openThreeDotMenu {
}.openSettings {
}.openLoginsAndPasswordSubMenu {
@@ -274,7 +276,7 @@ class SettingsPrivacyTest {
browserScreen {
}.openTabDrawer {
verifyPrivateModeSelected()
- }.openHomeScreen { }
+ }.openNewTab { }.dismiss { }
setOpenLinksInPrivateOff()
@@ -321,7 +323,7 @@ class SettingsPrivacyTest {
clickAddAutomaticallyButton()
}.openHomeScreenShortcut(pageShortcutName) {
}.openTabDrawer {
- }.openHomeScreen { }
+ }.openNewTab { }.dismiss { }
setOpenLinksInPrivateOff()
restartApp(activityTestRule)
@@ -331,7 +333,8 @@ class SettingsPrivacyTest {
}.searchAndOpenHomeScreenShortcut(pageShortcutName) {
}.openTabDrawer {
verifyNormalModeSelected()
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
}.openThreeDotMenu {
}.openSettings {
}.openPrivateBrowsingSubMenu {
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 802a3a22b..086d08186 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt
@@ -4,7 +4,6 @@
package org.mozilla.fenix.ui
-import androidx.core.net.toUri
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import okhttp3.mockwebserver.MockWebServer
@@ -16,6 +15,7 @@ import org.junit.Test
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.helpers.TestAssetHelper
+import org.mozilla.fenix.ui.robots.clickUrlbar
import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.navigationToolbar
@@ -57,7 +57,8 @@ class SmokeTest {
}.goBackToWebsite {
}.openTabDrawer {
verifyExistingTabList()
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
verifyHomeScreen()
}
}
@@ -105,7 +106,8 @@ class SmokeTest {
}.addToFirefoxHome {
verifySnackBarText("Added to top sites!")
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
verifyExistingTopSitesTabs(defaultWebPage.title)
}.openTabDrawer {
}.openTab(defaultWebPage.title) {
@@ -131,13 +133,11 @@ class SmokeTest {
verifyUrl(defaultWebPage.url.toString())
}.openTabDrawer {
closeTabViaXButton(defaultWebPage.title)
- }.openHomeScreen {
- navigationToolbar {
- }.enterURLAndEnterToBrowser(youtubeUrl.toUri()) {
- verifyBlueDot()
- }.openThreeDotMenu {
- verifyOpenInAppButton()
- }
+ }.openNewTab {
+ }.submitQuery(youtubeUrl) {
+ verifyBlueDot()
+ }.openThreeDotMenu {
+ verifyOpenInAppButton()
}
}
@@ -184,7 +184,8 @@ class SmokeTest {
}.addToFirefoxHome {
verifySnackBarText("Added to top sites!")
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
togglePrivateBrowsingModeOnOff()
verifyExistingTopSitesTabs(defaultWebPage.title)
togglePrivateBrowsingModeOnOff()
@@ -208,13 +209,11 @@ class SmokeTest {
verifyUrl(defaultWebPage.url.toString())
}.openTabDrawer {
closeTabViaXButton(defaultWebPage.title)
- }.openHomeScreen {
- navigationToolbar {
- }.enterURLAndEnterToBrowser(youtubeUrl.toUri()) {
- verifyBlueDot()
- }.openThreeDotMenu {
- verifyOpenInAppButton()
- }
+ }.openNewTab {
+ }.submitQuery(youtubeUrl) {
+ verifyBlueDot()
+ }.openThreeDotMenu {
+ verifyOpenInAppButton()
}
}
}
@@ -239,7 +238,8 @@ class SmokeTest {
verifyUrl("webcompat.com/issues/new")
verifyTabCounter("2")
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
}.openThreeDotMenu {
}.openSettings {
}.openEnhancedTrackingProtectionSubMenu {
@@ -254,4 +254,60 @@ class SmokeTest {
}
}
}
+
+ @Test
+ fun verifySearchEngineCanBeChangedTemporarilyUsingShortcuts() {
+ val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
+
+ homeScreen {
+ }.openSearch {
+ verifyKeyboardVisibility()
+ clickSearchEngineButton()
+ verifySearchEngineList()
+ changeDefaultSearchEngine("Amazon.com")
+ verifySearchEngineIcon("Amazon.com")
+ }.goToSearchEngine {
+ }.enterURLAndEnterToBrowser(defaultWebPage.url) {
+ }.openTabDrawer {
+ }.openNewTab {
+ clickSearchEngineButton()
+ mDevice.waitForIdle()
+ changeDefaultSearchEngine("Bing")
+ verifySearchEngineIcon("Bing")
+ }.goToSearchEngine {
+ }.enterURLAndEnterToBrowser(defaultWebPage.url) {
+ }.openTabDrawer {
+ }.openNewTab {
+ clickSearchEngineButton()
+ mDevice.waitForIdle()
+ changeDefaultSearchEngine("DuckDuckGo")
+ verifySearchEngineIcon("DuckDuckGo")
+ }.goToSearchEngine {
+ }.enterURLAndEnterToBrowser(defaultWebPage.url) {
+ }.openTabDrawer {
+ }.openNewTab {
+ clickSearchEngineButton()
+ mDevice.waitForIdle()
+ changeDefaultSearchEngine("Twitter")
+ verifySearchEngineIcon("Twitter")
+ }.goToSearchEngine {
+ }.enterURLAndEnterToBrowser(defaultWebPage.url) {
+ }.openTabDrawer {
+ }.openNewTab {
+ clickSearchEngineButton()
+ changeDefaultSearchEngine("Wikipedia")
+ verifySearchEngineIcon("Wikipedia")
+ }.goToSearchEngine {
+ }.enterURLAndEnterToBrowser(defaultWebPage.url) {
+ }.openTabDrawer {
+ // Checking whether the next search will be with default or not
+ }.openNewTab {
+ }.goToSearchEngine {
+ }.enterURLAndEnterToBrowser(defaultWebPage.url) {
+ }.openNavigationToolbar {
+ clickUrlbar {
+ verifyDefaultSearchEngine("Google")
+ }
+ }
+ }
}
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 059d47e4f..02a270524 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt
@@ -149,92 +149,87 @@ class TabbedBrowsingTest {
@Test
fun closeTabTest() {
- var genericURLS = TestAssetHelper.getGenericAssets(mockWebServer)
-
- genericURLS.forEachIndexed { index, element ->
- navigationToolbar {
- }.openNewTabAndEnterToBrowser(element.url) {
- }.openTabDrawer {
- verifyExistingOpenTabs("Test_Page_${index + 1}")
- verifyCloseTabsButton("Test_Page_${index + 1}")
- closeTabViaXButton("Test_Page_${index + 1}")
- verifySnackBarText("Tab closed")
- snackBarButtonClick("UNDO")
- }
+ val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1)
- mDevice.waitForIdle()
-
- browserScreen {
- }.openTabDrawer {
- verifyExistingOpenTabs("Test_Page_${index + 1}")
- swipeTabRight("Test_Page_${index + 1}")
- verifySnackBarText("Tab closed")
- snackBarButtonClick("UNDO")
- }
+ navigationToolbar {
+ }.openNewTabAndEnterToBrowser(genericURL.url) {
+ }.openTabDrawer {
+ verifyExistingOpenTabs("Test_Page_1")
+ verifyCloseTabsButton("Test_Page_1")
+ closeTabViaXButton("Test_Page_1")
+ verifySnackBarText("Tab closed")
+ snackBarButtonClick("UNDO")
+ }
- mDevice.waitForIdle()
+ mDevice.waitForIdle()
- browserScreen {
- }.openTabDrawer {
- verifyExistingOpenTabs("Test_Page_${index + 1}")
- swipeTabLeft("Test_Page_${index + 1}")
- verifySnackBarText("Tab closed")
- snackBarButtonClick("UNDO")
- }
+ browserScreen {
+ }.openTabDrawer {
+ verifyExistingOpenTabs("Test_Page_1")
+ swipeTabRight("Test_Page_1")
+ verifySnackBarText("Tab closed")
+ snackBarButtonClick("UNDO")
+ }
- mDevice.waitForIdle()
+ mDevice.waitForIdle()
- browserScreen {
- }.openTabDrawer {
- verifyExistingOpenTabs("Test_Page_${index + 1}")
- }.openHomeScreen {
- }
+ browserScreen {
+ }.openTabDrawer {
+ verifyExistingOpenTabs("Test_Page_1")
+ swipeTabLeft("Test_Page_1")
+ verifySnackBarText("Tab closed")
+ snackBarButtonClick("UNDO")
}
+
+ mDevice.waitForIdle()
+
+ browserScreen {
+ }.openTabDrawer {
+ verifyExistingOpenTabs("Test_Page_1")
+ }.openNewTab {
+ }.dismiss { }
}
@Test
fun closePrivateTabTest() {
- var genericURLS = TestAssetHelper.getGenericAssets(mockWebServer)
+ val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1)
homeScreen { }.togglePrivateBrowsingMode()
- genericURLS.forEachIndexed { index, element ->
- navigationToolbar {
- }.openNewTabAndEnterToBrowser(element.url) {
- }.openTabDrawer {
- verifyExistingOpenTabs("Test_Page_${index + 1}")
- verifyCloseTabsButton("Test_Page_${index + 1}")
- closeTabViaXButton("Test_Page_${index + 1}")
- verifySnackBarText("Private tab closed")
- snackBarButtonClick("UNDO")
- }
+ navigationToolbar {
+ }.openNewTabAndEnterToBrowser(genericURL.url) {
+ }.openTabDrawer {
+ verifyExistingOpenTabs("Test_Page_1")
+ verifyCloseTabsButton("Test_Page_1")
+ closeTabViaXButton("Test_Page_1")
+ verifySnackBarText("Private tab closed")
+ snackBarButtonClick("UNDO")
+ }
- mDevice.waitForIdle()
+ mDevice.waitForIdle()
- browserScreen {
- }.openTabDrawer {
- verifyExistingOpenTabs("Test_Page_${index + 1}")
- swipeTabRight("Test_Page_${index + 1}")
- verifySnackBarText("Private tab closed")
- snackBarButtonClick("UNDO")
- }
+ browserScreen {
+ }.openTabDrawer {
+ verifyExistingOpenTabs("Test_Page_1")
+ swipeTabRight("Test_Page_1")
+ verifySnackBarText("Private tab closed")
+ snackBarButtonClick("UNDO")
+ }
- mDevice.waitForIdle()
+ mDevice.waitForIdle()
- browserScreen {
- }.openTabDrawer {
- verifyExistingOpenTabs("Test_Page_${index + 1}")
- swipeTabLeft("Test_Page_${index + 1}")
- verifySnackBarText("Private tab closed")
- snackBarButtonClick("UNDO")
- }
+ browserScreen {
+ }.openTabDrawer {
+ verifyExistingOpenTabs("Test_Page_1")
+ swipeTabLeft("Test_Page_1")
+ verifySnackBarText("Private tab closed")
+ snackBarButtonClick("UNDO")
+ }
- mDevice.waitForIdle()
+ mDevice.waitForIdle()
- browserScreen {
- }.openTabDrawer {
- verifyExistingOpenTabs("Test_Page_${index + 1}")
- closeTabViaXButton("Test_Page_${index + 1}")
- }
+ browserScreen {
+ }.openTabDrawer {
+ verifyExistingOpenTabs("Test_Page_1")
}
}
@@ -290,8 +285,8 @@ class TabbedBrowsingTest {
verifyTabTrayOverflowMenu(true)
verifyExistingOpenTabs(defaultWebPage.title)
verifyCloseTabsButton(defaultWebPage.title)
- }.openHomeScreen {
- }
+ }.openNewTab {
+ }.dismiss { }
}
@Test
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt
index 67f3020bc..59e392a06 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt
@@ -68,13 +68,14 @@ class ThreeDotMenuMainTest {
}.openHelp {
verifyHelpUrl()
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
}.openThreeDotMenu {
}.openWhatsNew {
verifyWhatsNewURL()
}.openTabDrawer {
- }.openHomeScreen {
- }
+ }.openNewTab {
+ }.dismiss { }
homeScreen {
}.openThreeDotMenu {
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt
index 697e25ca6..674e767c2 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TopSitesTest.kt
@@ -58,7 +58,8 @@ class TopSitesTest {
}.addToFirefoxHome {
verifySnackBarText("Added to top sites!")
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
verifyExistingTopSitesList()
verifyExistingTopSitesTabs(defaultWebPageTitle)
}
@@ -76,13 +77,15 @@ class TopSitesTest {
}.addToFirefoxHome {
verifySnackBarText("Added to top sites!")
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
verifyExistingTopSitesList()
verifyExistingTopSitesTabs(defaultWebPageTitle)
}.openTopSiteTabWithTitle(title = defaultWebPageTitle) {
verifyUrl(defaultWebPage.url.toString().replace("http://", ""))
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
verifyExistingTopSitesList()
verifyExistingTopSitesTabs(defaultWebPageTitle)
}.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) {
@@ -105,7 +108,8 @@ class TopSitesTest {
}.addToFirefoxHome {
verifySnackBarText("Added to top sites!")
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
verifyExistingTopSitesList()
verifyExistingTopSitesTabs(defaultWebPageTitle)
}.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) {
@@ -127,7 +131,8 @@ class TopSitesTest {
}.addToFirefoxHome {
verifySnackBarText("Added to top sites!")
}.openTabDrawer {
- }.openHomeScreen {
+ }.openNewTab {
+ }.dismiss {
verifyExistingTopSitesList()
verifyExistingTopSitesTabs(defaultWebPageTitle)
}.openContextMenuOnTopSitesWithTitle(defaultWebPageTitle) {
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BookmarksRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BookmarksRobot.kt
index 3a37b6978..bd5a17f06 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BookmarksRobot.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BookmarksRobot.kt
@@ -35,6 +35,7 @@ import org.hamcrest.Matchers.containsString
import org.junit.Assert.assertEquals
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.TestAssetHelper
+import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.ext.waitNotNull
@@ -52,10 +53,7 @@ class BookmarksRobot {
fun verifyBookmarkedURL(url: String) = assertBookmarkURL(url)
fun verifyFolderTitle(title: String) {
- mDevice.waitNotNull(
- Until.findObject(text(title)),
- TestAssetHelper.waitingTime
- )
+ mDevice.findObject(UiSelector().text(title)).waitForExists(waitingTime)
assertFolderTitle(title)
}
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 5d6732cce..e07d7ba8c 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
@@ -12,12 +12,14 @@ import android.net.Uri
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.action.ViewActions
+import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.BundleMatchers
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers
+import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
index 5f4377ff8..48ccc4c2e 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
@@ -37,8 +37,6 @@ class DownloadRobot {
fun verifyDownloadNotificationPopup() = assertDownloadNotificationPopup()
- fun verifyDownloadNotificationShade() = assertDownloadNotificationShade()
-
fun verifyPhotosAppOpens() = assertPhotosOpens()
class Transition {
@@ -98,17 +96,6 @@ private fun assertDownloadPrompt() {
mDevice.waitNotNull(Until.findObjects(By.res("org.mozilla.fenix.debug:id/download_button")))
}
-private fun assertDownloadNotificationShade() {
- val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
- mDevice.openNotification()
- mDevice.waitNotNull(
- Until.findObjects(By.text("Download completed")), TestAssetHelper.waitingTime
- )
-
- // Go home (no UIDevice closeNotification) to close notification shade
- mDevice.pressHome()
-}
-
private fun assertDownloadNotificationPopup() {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
mDevice.waitNotNull(Until.findObjects(By.text("Open")), TestAssetHelper.waitingTime)
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 f1e24be71..8540e2a0a 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
@@ -553,11 +553,11 @@ private fun assertWelcomeHeader() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertGetTheMostHeader() =
- onView(allOf(withText("Get the most out of Firefox Preview.")))
+ onView(allOf(withText("Start syncing bookmarks, passwords, and more with your Firefox account.")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertAccountsSignInButton() =
- onView(ViewMatchers.withResourceName("turn_on_sync_button"))
+ onView(ViewMatchers.withResourceName("fxa_sign_in_button"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertGetToKnowHeader() =
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NavigationToolbarRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NavigationToolbarRobot.kt
index 556007c8f..184bf7eaf 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NavigationToolbarRobot.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NavigationToolbarRobot.kt
@@ -26,6 +26,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
+import kotlinx.android.synthetic.main.fragment_search_dialog.view.*
import org.hamcrest.CoreMatchers.anyOf
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.not
@@ -239,12 +240,18 @@ fun navigationToolbar(interact: NavigationToolbarRobot.() -> Unit): NavigationTo
return NavigationToolbarRobot.Transition()
}
+fun clickUrlbar(interact: SearchRobot.() -> Unit): SearchRobot.Transition {
+ urlBar().click()
+ SearchRobot().interact()
+ return SearchRobot.Transition()
+}
+
private fun assertSuggestionsAreEqualTo(suggestionSize: Int, searchTerm: String) {
mDevice.waitForIdle()
awesomeBar().perform(typeText(searchTerm))
mDevice.waitForIdle()
- onView(withId(R.id.awesomeBar)).check(suggestionsAreEqualTo(suggestionSize))
+ onView(withId(R.id.awesome_bar)).check(suggestionsAreEqualTo(suggestionSize))
}
private fun assertSuggestionsAreMoreThan(suggestionSize: Int, searchTerm: String) {
@@ -252,7 +259,7 @@ private fun assertSuggestionsAreMoreThan(suggestionSize: Int, searchTerm: String
awesomeBar().perform(typeText(searchTerm))
mDevice.waitForIdle()
- onView(withId(R.id.awesomeBar)).check(suggestionsAreGreaterThan(suggestionSize))
+ onView(withId(R.id.awesome_bar)).check(suggestionsAreGreaterThan(suggestionSize))
}
private fun assertNoHistoryBookmarks() {
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NotificationRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NotificationRobot.kt
index df3e5c6fc..aea828072 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NotificationRobot.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/NotificationRobot.kt
@@ -1,11 +1,12 @@
package org.mozilla.fenix.ui.robots
-import android.content.res.Resources
import androidx.test.uiautomator.By.text
+import androidx.test.uiautomator.UiObjectNotFoundException
import androidx.test.uiautomator.UiScrollable
import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.Until
import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
import org.mozilla.fenix.helpers.ext.waitNotNull
@@ -17,23 +18,21 @@ class NotificationRobot {
UiSelector().resourceId("com.android.systemui:id/notification_stack_scroller")
)
- mDevice.waitNotNull(
- Until.hasObject(text(notificationMessage)),
- waitingTime
- )
+ val notificationFound: Boolean
+
+ notificationFound = try {
+ notificationTray().getChildByText(
+ UiSelector().text(notificationMessage), notificationMessage, true
+ ).exists()
+ } catch (e: UiObjectNotFoundException) {
+ false
+ }
- var notificationFound = false
- while (!notificationFound) {
- try {
- val notification = notificationTray().getChildByText(
- UiSelector().text(notificationMessage), notificationMessage,
- true
- )
- notification.exists()
- notificationFound = true
- } catch (e: Resources.NotFoundException) {
- e.printStackTrace()
- }
+ if (!notificationFound) {
+ // swipe 2 times to expand the silent notifications on API 28 and higher, single-swipe doesn't do it
+ notificationTray().swipeUp(2)
+ val notification = mDevice.findObject(UiSelector().textContains(notificationMessage))
+ assertTrue(notification.exists())
}
}
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt
index 8bfe0f56e..942848e76 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt
@@ -6,6 +6,7 @@
package org.mozilla.fenix.ui.robots
+import android.widget.ToggleButton
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.ViewInteraction
@@ -16,7 +17,9 @@ import androidx.test.espresso.action.ViewActions.typeText
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.ViewMatchers
+import androidx.test.espresso.matcher.ViewMatchers.Visibility
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
+import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.platform.app.InstrumentationRegistry
@@ -28,8 +31,11 @@ import androidx.test.uiautomator.Until
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.startsWith
import org.hamcrest.Matchers
+import org.junit.Assert.assertEquals
import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.TestAssetHelper
+import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
+import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.ext.waitNotNull
/**
@@ -47,6 +53,24 @@ class SearchRobot {
fun verifySearchSettings() = assertSearchSettings()
fun verifySearchBarEmpty() = assertSearchBarEmpty()
+ fun verifyKeyboardVisibility() = assertKeyboardVisibility(isExpectedToBeVisible = true)
+ fun verifySearchEngineList() = assertSearchEngineList()
+ fun verifySearchEngineIcon(expectedText: String) {
+ onView(withContentDescription(expectedText))
+ }
+ fun verifyDefaultSearchEngine(expectedText: String) = assertDefaultSearchEngine(expectedText)
+
+ fun changeDefaultSearchEngine(searchEngineName: String) =
+ selectDefaultSearchEngine(searchEngineName)
+
+ fun clickSearchEngineButton() {
+ val searchEngineButton = mDevice.findObject(UiSelector()
+ .instance(1)
+ .className(ToggleButton::class.java))
+ searchEngineButton.waitForExists(waitingTime)
+ searchEngineButton.click()
+ }
+
fun clickScanButton() {
scanButton().perform(click())
}
@@ -82,10 +106,10 @@ class SearchRobot {
fun scrollToSearchEngineSettings() {
// Soft keyboard is visible on screen on view access; hide it
- onView(allOf(withId(R.id.search_layout))).perform(
+ onView(allOf(withId(R.id.search_wrapper))).perform(
closeSoftKeyboard()
)
- onView(allOf(withId(R.id.awesomeBar))).perform(ViewActions.swipeUp())
+ onView(allOf(withId(R.id.awesome_bar))).perform(ViewActions.swipeUp())
}
fun clickSearchEngineSettings() {
@@ -99,6 +123,13 @@ class SearchRobot {
class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ fun dismiss(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
+ mDevice.waitForIdle()
+ mDevice.pressBack()
+ HomeScreenRobot().interact()
+ return HomeScreenRobot.Transition()
+ }
+
fun openBrowser(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
mDevice.waitForIdle()
browserToolbarEditView().perform(typeText("mozilla\n"))
@@ -106,10 +137,23 @@ class SearchRobot {
BrowserRobot().interact()
return BrowserRobot.Transition()
}
+
+ fun submitQuery(query: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
+ mDevice.waitForIdle()
+ browserToolbarEditView().perform(typeText(query + "\n"))
+
+ BrowserRobot().interact()
+ return BrowserRobot.Transition()
+ }
+
+ fun goToSearchEngine(interact: NavigationToolbarRobot.() -> Unit): NavigationToolbarRobot.Transition {
+ NavigationToolbarRobot().interact()
+ return NavigationToolbarRobot.Transition()
+ }
}
}
-private fun awesomeBar() = onView(withId(R.id.awesomeBar))
+private fun awesomeBar() = onView(withId(R.id.awesome_bar))
private fun browserToolbarEditView() =
onView(Matchers.allOf(withId(R.id.mozac_browser_toolbar_edit_url_view)))
@@ -136,6 +180,8 @@ private fun scanButton(): ViewInteraction {
private fun clearButton() = onView(withId(R.id.mozac_browser_toolbar_clear_view))
+private fun searchWrapper() = onView(withId(R.id.search_wrapper))
+
private fun assertSearchEngineURL(searchEngineName: String) {
mDevice.waitNotNull(
Until.findObject(By.textContains("${searchEngineName.toLowerCase()}.com/?q=mozilla")),
@@ -178,4 +224,46 @@ fun searchScreen(interact: SearchRobot.() -> Unit): SearchRobot.Transition {
return SearchRobot.Transition()
}
+private fun assertKeyboardVisibility(isExpectedToBeVisible: Boolean) = {
+ mDevice.waitNotNull(
+ Until.findObject(
+ By.text("Search Engine")
+ ), waitingTime
+ )
+ assertEquals(
+ isExpectedToBeVisible,
+ UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ .executeShellCommand("dumpsys input_method | grep mInputShown")
+ .contains("mInputShown=true")
+ )
+}
+
+private fun assertSearchEngineList() {
+ onView(withId(R.id.mozac_browser_toolbar_edit_icon)).click()
+ onView(withText("Google"))
+ .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
+ onView(withText("Amazon.com"))
+ .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
+ onView(withText("Bing"))
+ .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
+ onView(withText("DuckDuckGo"))
+ .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
+ onView(withText("Twitter"))
+ .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
+ onView(withText("Wikipedia"))
+ .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
+}
+
+private fun selectDefaultSearchEngine(searchEngine: String) {
+ onView(withId(R.id.mozac_browser_toolbar_edit_icon)).click()
+ onView(withText(searchEngine))
+ .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
+ .perform(click())
+}
+
+private fun assertDefaultSearchEngine(expectedText: String) {
+ onView(allOf(withId(R.id.mozac_browser_toolbar_edit_icon), withContentDescription(expectedText)))
+ .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
+}
+
private fun goBackButton() = onView(allOf(withContentDescription("Navigate up")))
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt
index 4e6a9e3ef..fe6986fe4 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAboutRobot.kt
@@ -222,7 +222,7 @@ private fun assertLibrariesUsed() {
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
.perform(click())
- onView(withId(R.id.action_bar)).check(matches(hasDescendant(withText(containsString("Firefox Preview | OSS Libraries")))))
+ onView(withId(R.id.navigationToolbar)).check(matches(hasDescendant(withText(containsString("Firefox Preview | OSS Libraries")))))
Espresso.pressBack()
}
diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/TabDrawerRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/TabDrawerRobot.kt
index 8acea8478..d13772c9c 100644
--- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/TabDrawerRobot.kt
+++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/TabDrawerRobot.kt
@@ -135,12 +135,12 @@ class TabDrawerRobot {
return BrowserRobot.Transition()
}
- fun openHomeScreen(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
+ fun openNewTab(interact: SearchRobot.() -> Unit): SearchRobot.Transition {
mDevice.waitForIdle()
newTabButton().perform(click())
- HomeScreenRobot().interact()
- return HomeScreenRobot.Transition()
+ SearchRobot().interact()
+ return SearchRobot.Transition()
}
fun toggleToNormalTabs(interact: TabDrawerRobot.() -> Unit): Transition {
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 47ab12921..d77ab3513 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
@@ -164,7 +164,7 @@ class ThreeDotMenuMainRobot {
fun openBookmarks(interact: BookmarksRobot.() -> Unit): BookmarksRobot.Transition {
onView(withId(R.id.mozac_browser_menu_recyclerView)).perform(ViewActions.swipeDown())
- mDevice.waitNotNull(Until.findObject(By.text("Bookmarks")), waitingTime)
+ mDevice.findObject(UiSelector().resourceId("R.id.bookmark_list")).waitForExists(waitingTime)
bookmarksButton().click()
BookmarksRobot().interact()
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 554e11126..78a517e81 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -215,7 +215,8 @@
android:name=".crashes.CrashListActivity"
android:exported="false" />
-
+ = Build.VERSION_CODES.O) { // required by StorageStatsMetrics.
- taskQueue.runIfReadyOrQueue {
+ queue.runIfReadyOrQueue {
// Because it may be slow to capture the storage stats, it might be preferred to
// create a WorkManager task for this metric, however, I ran out of
// implementation time and WorkManager is harder to test.
@@ -217,6 +220,12 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
}
}
+ fun queueReviewPrompt() {
+ GlobalScope.launch(Dispatchers.IO) {
+ components.reviewPromptController.trackApplicationLaunch()
+ }
+ }
+
initQueue()
// We init these items in the visual completeness queue to avoid them initing in the critical
@@ -224,6 +233,7 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
queueInitExperiments()
queueInitStorageAndServices()
queueMetrics()
+ queueReviewPrompt()
}
private fun startMetricsIfEnabled() {
@@ -257,14 +267,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
// no-op, LeakCanary is disabled by default
}
- // This is for issue https://github.com/mozilla-mobile/fenix/issues/11660. We prefetch our info for startup
- // so that we're sure that we have all the data available as our fragment is launched.
- private fun prefetchForHomeFragment() {
- StrictMode.allowThreadDiskReads().resetPoliciesAfter {
- components.core.topSiteStorage.prefetch()
- }
- }
-
private fun setupPush() {
// Sets the PushFeature as the singleton instance for push messages to go to.
// We need the push feature setup here to deliver messages in the case where the service
@@ -318,7 +320,7 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
runOnlyInMainProcess {
components.core.icons.onTrimMemory(level)
- components.core.sessionManager.onTrimMemory(level)
+ components.core.store.dispatch(SystemAction.LowMemoryAction(level))
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/GlobalDirections.kt b/app/src/main/java/org/mozilla/fenix/GlobalDirections.kt
index eb64e044b..0e31e5d93 100644
--- a/app/src/main/java/org/mozilla/fenix/GlobalDirections.kt
+++ b/app/src/main/java/org/mozilla/fenix/GlobalDirections.kt
@@ -44,7 +44,7 @@ enum class GlobalDirections(val navDirections: NavDirections, val destinationId:
R.id.deleteBrowsingDataFragment
),
SettingsAddonManager(
- NavGraphDirections.actionGlobalSettingsAddonsManagementFragment(),
+ NavGraphDirections.actionGlobalAddonsManagementFragment(),
R.id.addonsManagementFragment
),
SettingsLogins(
diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
index 02496dca7..bc49ff745 100644
--- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
+++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
@@ -6,6 +6,7 @@ package org.mozilla.fenix
import android.content.Context
import android.content.Intent
+import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.os.StrictMode
@@ -21,7 +22,6 @@ import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.PROTECTED
import androidx.appcompat.app.ActionBar
import androidx.appcompat.widget.Toolbar
-import androidx.core.view.doOnPreDraw
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavDestination
import androidx.navigation.NavDirections
@@ -40,13 +40,11 @@ import mozilla.components.browser.search.SearchEngine
import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.state.state.WebExtensionState
-import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineView
import mozilla.components.feature.contextmenu.DefaultSelectionActionDelegate
import mozilla.components.feature.privatemode.notification.PrivateNotificationFeature
import mozilla.components.feature.search.BrowserStoreSearchAdapter
-import mozilla.components.feature.search.SearchAdapter
import mozilla.components.service.fxa.sync.SyncReason
import mozilla.components.support.base.feature.UserInteractionHandler
import mozilla.components.support.ktx.android.arch.lifecycle.addObservers
@@ -56,7 +54,6 @@ import mozilla.components.support.ktx.android.content.share
import mozilla.components.support.ktx.kotlin.isUrl
import mozilla.components.support.ktx.kotlin.toNormalizedUrl
import mozilla.components.support.locale.LocaleAwareAppCompatActivity
-import mozilla.components.support.utils.RunWhenReadyQueue
import mozilla.components.support.utils.SafeIntent
import mozilla.components.support.utils.toSafeIntent
import mozilla.components.support.webextensions.WebExtensionPopupFeature
@@ -71,6 +68,7 @@ import org.mozilla.fenix.components.metrics.BreadcrumbsRecorder
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.exceptions.trackingprotection.TrackingProtectionExceptionsFragmentDirections
import org.mozilla.fenix.ext.alreadyOnDestination
+import org.mozilla.fenix.ext.breadcrumb
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.nav
@@ -100,9 +98,11 @@ import org.mozilla.fenix.settings.search.EditCustomSearchEngineFragmentDirection
import org.mozilla.fenix.share.AddNewDeviceFragmentDirections
import org.mozilla.fenix.sync.SyncedTabsFragmentDirections
import org.mozilla.fenix.tabtray.TabTrayDialogFragment
+import org.mozilla.fenix.tabtray.TabTrayDialogFragmentDirections
import org.mozilla.fenix.theme.DefaultThemeManager
import org.mozilla.fenix.theme.ThemeManager
import org.mozilla.fenix.utils.BrowsersCache
+import java.lang.ref.WeakReference
/**
* The main activity of the application. The application is primarily a single Activity (this one)
@@ -121,7 +121,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
private var isVisuallyComplete = false
- private var visualCompletenessQueue: RunWhenReadyQueue? = null
private var privateNotificationObserver: PrivateNotificationFeature? = null
private var isToolbarInflated = false
@@ -156,6 +155,16 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
super.onCreate(savedInstanceState)
}
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onCreate()",
+ data = mapOf(
+ "recreated" to (savedInstanceState != null).toString(),
+ "intent" to (intent?.action ?: "null")
+ )
+ )
+
components.publicSuffixList.prefetch()
setupThemeAndBrowsingMode(getModeFromIntentOrLastKnown(intent))
@@ -163,13 +172,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
// Must be after we set the content view
if (isVisuallyComplete) {
- rootContainer.doOnPreDraw {
- // This delay is temporary. We are delaying 5 seconds until the performance
- // team can locate the real point of visual completeness.
- it.postDelayed({
- visualCompletenessQueue!!.ready()
- }, delay)
- }
+ components.performance.visualCompletenessQueue
+ .attachViewToRunVisualCompletenessQueueLater(WeakReference(rootContainer))
}
sessionObserver = UriOpenedObserver(this)
@@ -226,19 +230,33 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
captureSnapshotTelemetryMetrics()
- setAppAllStartTelemetry(intent.toSafeIntent())
+ startupTelemetryOnCreateCalled(intent.toSafeIntent(), savedInstanceState != null)
StartupTimeline.onActivityCreateEndHome(this) // DO NOT MOVE ANYTHING BELOW HERE.
}
- protected open fun setAppAllStartTelemetry(safeIntent: SafeIntent) {
- components.appAllSourceStartTelemetry.receivedIntentInHomeActivity(safeIntent)
+ protected open fun startupTelemetryOnCreateCalled(safeIntent: SafeIntent, hasSavedInstanceState: Boolean) {
+ components.appStartupTelemetry.onHomeActivityOnCreate(safeIntent, hasSavedInstanceState)
+ }
+
+ override fun onRestart() {
+ super.onRestart()
+
+ components.appStartupTelemetry.onHomeActivityOnRestart()
}
@CallSuper
override fun onResume() {
super.onResume()
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onResume()"
+ )
+
+ components.appStartupTelemetry.onHomeActivityOnResume()
+
components.backgroundServices.accountManagerAvailableQueue.runIfReadyOrQueue {
lifecycleScope.launch {
// Make sure accountManager is initialized.
@@ -266,6 +284,29 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
}
}
+ override fun onStart() {
+ super.onStart()
+
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onStart()"
+ )
+ }
+
+ override fun onStop() {
+ super.onStop()
+
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onStop()",
+ data = mapOf(
+ "finishing" to isFinishing.toString()
+ )
+ )
+ }
+
final override fun onPause() {
if (settings().lastKnownMode.isPrivate) {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
@@ -273,6 +314,15 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
super.onPause()
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onPause()",
+ data = mapOf(
+ "finishing" to isFinishing.toString()
+ )
+ )
+
// Every time the application goes into the background, it is possible that the user
// is about to change the browsers installed on their system. Therefore, we reset the cache of
// all the installed browsers.
@@ -283,9 +333,39 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
override fun onDestroy() {
super.onDestroy()
+
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onDestroy()",
+ data = mapOf(
+ "finishing" to isFinishing.toString()
+ )
+ )
+
privateNotificationObserver?.stop()
}
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onConfigurationChanged()"
+ )
+ }
+
+ override fun recreate() {
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "recreate()"
+ )
+
+ super.recreate()
+ }
+
/**
* Handles intents received when the activity is open.
*/
@@ -293,6 +373,15 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
super.onNewIntent(intent)
intent ?: return
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onNewIntent()",
+ data = mapOf(
+ "intent" to intent.action.toString()
+ )
+ )
+
val intentProcessors =
listOf(CrashReporterIntentProcessor()) + externalSourceIntentProcessors
val intentHandled =
@@ -317,7 +406,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
.let(::getIntentAllSource)
?.also { components.analytics.metrics.track(Event.AppReceivedIntent(it)) }
- setAppAllStartTelemetry(intent.toSafeIntent())
+ components.appStartupTelemetry.onHomeActivityOnNewIntent(intent.toSafeIntent())
}
/**
@@ -331,7 +420,10 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
): View? = when (name) {
EngineView::class.java.name -> components.core.engine.createView(context, attrs).apply {
selectionActionDelegate = DefaultSelectionActionDelegate(
- getSearchAdapter(components.core.store),
+ BrowserStoreSearchAdapter(
+ components.core.store,
+ tabId = getIntentSessionId(intent.toSafeIntent())
+ ),
resources = context.resources,
shareTextClicked = { share(it) },
emailTextClicked = { email(it) },
@@ -424,9 +516,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
super.onUserLeaveHint()
}
- protected open fun getSearchAdapter(store: BrowserStore): SearchAdapter =
- BrowserStoreSearchAdapter(store)
-
protected open fun getBreadcrumbMessage(destination: NavDestination): String {
val fragmentName = resources.getResourceEntryName(destination.id)
return "Changing to fragment $fragmentName, isCustomTab: false"
@@ -597,6 +686,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
AddonPermissionsDetailsFragmentDirections.actionGlobalBrowser(customTabSessionId)
BrowserDirection.FromLoginDetailFragment ->
LoginDetailFragmentDirections.actionGlobalBrowser(customTabSessionId)
+ BrowserDirection.FromTabTray ->
+ TabTrayDialogFragmentDirections.actionGlobalBrowser(customTabSessionId)
}
/**
@@ -675,9 +766,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
* The root container is null at this point, so let the HomeActivity know that
* we are visually complete.
*/
- fun postVisualCompletenessQueue(visualCompletenessQueue: RunWhenReadyQueue) {
+ fun setVisualCompletenessQueueReady() {
isVisuallyComplete = true
- this.visualCompletenessQueue = visualCompletenessQueue
}
private fun captureSnapshotTelemetryMetrics() = CoroutineScope(Dispatchers.IO).launch {
@@ -714,7 +804,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
const val PRIVATE_BROWSING_MODE = "private_browsing_mode"
const val EXTRA_DELETE_PRIVATE_TABS = "notification_delete_and_open"
const val EXTRA_OPENED_FROM_NOTIFICATION = "notification_open"
- const val delay = 5000L
const val START_IN_RECENTS_SCREEN = "start_in_recents_screen"
// PWA must have been used within last 30 days to be considered "recently used" for the
diff --git a/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt b/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt
index e6bc5e57b..25fa9fc40 100644
--- a/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt
@@ -66,6 +66,12 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management)
}
}
+ override fun onDestroyView() {
+ super.onDestroyView()
+ // letting go of the resources to avoid memory leak.
+ adapter = null
+ }
+
private fun bindRecyclerView(view: View) {
val managementView = AddonsManagementView(
navController = findNavController(),
@@ -120,7 +126,6 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management)
addonNameTextColor = ThemeManager.resolveAttribute(R.attr.primaryText, context),
addonSummaryTextColor = ThemeManager.resolveAttribute(R.attr.secondaryText, context),
sectionsTypeFace = ResourcesCompat.getFont(context, R.font.metropolis_semibold),
- addonBackgroundIconColor = ThemeManager.resolveAttribute(R.attr.inset, requireContext()),
addonAllowPrivateBrowsingLabelDrawableRes = R.drawable.ic_add_on_private_browsing_label
)
}
diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
index 8a27c69ad..2cb6cc002 100644
--- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
@@ -42,6 +42,7 @@ import mozilla.components.browser.state.selector.findTabOrCustomTabOrSelectedTab
import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.state.state.content.DownloadState
import mozilla.components.browser.state.store.BrowserStore
+import mozilla.components.browser.thumbnails.BrowserThumbnails
import mozilla.components.concept.engine.prompt.ShareData
import mozilla.components.feature.accounts.FxaCapability
import mozilla.components.feature.accounts.FxaWebChannelFeature
@@ -56,10 +57,10 @@ import mozilla.components.feature.privatemode.feature.SecureWindowFeature
import mozilla.components.feature.prompts.PromptFeature
import mozilla.components.feature.prompts.share.ShareDelegate
import mozilla.components.feature.readerview.ReaderViewFeature
+import mozilla.components.feature.search.SearchFeature
import mozilla.components.feature.session.FullScreenFeature
import mozilla.components.feature.session.PictureInPictureFeature
import mozilla.components.feature.session.SessionFeature
-import mozilla.components.feature.session.SessionUseCases
import mozilla.components.feature.session.SwipeRefreshFeature
import mozilla.components.feature.session.behavior.EngineViewBottomBehavior
import mozilla.components.feature.sitepermissions.SitePermissions
@@ -98,6 +99,7 @@ import org.mozilla.fenix.downloads.DownloadService
import org.mozilla.fenix.downloads.DynamicDownloadDialog
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.ext.accessibilityManager
+import org.mozilla.fenix.ext.breadcrumb
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.enterToImmersiveMode
import org.mozilla.fenix.ext.getPreferenceKey
@@ -133,6 +135,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
get() = _browserToolbarView!!
protected val readerViewFeature = ViewBoundFeatureWrapper()
+ protected val thumbnailsFeature = ViewBoundFeatureWrapper()
private val sessionFeature = ViewBoundFeatureWrapper()
private val contextMenuFeature = ViewBoundFeatureWrapper()
@@ -150,6 +153,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
private val secureWindowFeature = ViewBoundFeatureWrapper()
private var fullScreenMediaFeature =
ViewBoundFeatureWrapper()
+ private val searchFeature = ViewBoundFeatureWrapper()
private var pipFeature: PictureInPictureFeature? = null
var customTabSessionId: String? = null
@@ -169,11 +173,16 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
require(arguments != null)
customTabSessionId = arguments?.getString(EXTRA_SESSION_ID)
- val view = if (FeatureFlags.browserChromeGestures) {
- inflater.inflate(R.layout.browser_gesture_wrapper, container, false)
- } else {
- inflater.inflate(R.layout.fragment_browser, container, false)
- }
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onCreateView()",
+ data = mapOf(
+ "customTabSessionId" to customTabSessionId.toString()
+ )
+ )
+
+ val view = inflater.inflate(R.layout.fragment_browser, container, false)
val activity = activity as HomeActivity
activity.themeManager.applyStatusBarTheme(activity)
@@ -212,6 +221,11 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
}
return getSessionById()?.also { session ->
+ val openInFenixIntent = Intent(context, IntentReceiverActivity::class.java).apply {
+ action = Intent.ACTION_VIEW
+ putExtra(HomeActivity.OPEN_TO_BROWSER, true)
+ }
+
val browserToolbarController = DefaultBrowserToolbarController(
activity = requireActivity() as HomeActivity,
navController = findNavController(),
@@ -227,15 +241,12 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
swipeRefresh = swipeRefresh,
browserAnimator = browserAnimator,
customTabSession = customTabSessionId?.let { sessionManager.findSessionById(it) },
- openInFenixIntent = Intent(context, IntentReceiverActivity::class.java).apply {
- action = Intent.ACTION_VIEW
- putExtra(HomeActivity.OPEN_TO_BROWSER, true)
- },
+ openInFenixIntent = openInFenixIntent,
bookmarkTapped = { viewLifecycleOwner.lifecycleScope.launch { bookmarkTapped(it) } },
scope = viewLifecycleOwner.lifecycleScope,
tabCollectionStorage = requireComponents.core.tabCollectionStorage,
- topSiteStorage = requireComponents.core.topSiteStorage,
onTabCounterClicked = {
+ thumbnailsFeature.get()?.requestScreenshot()
findNavController().nav(
R.id.browserFragment,
BrowserFragmentDirections.actionGlobalTabTrayDialogFragment()
@@ -477,7 +488,16 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
},
onNeedToRequestPermissions = { permissions ->
requestPermissions(permissions, REQUEST_CODE_PROMPT_PERMISSIONS)
- }),
+ },
+ loginPickerView = if (FeatureFlags.loginSelect) loginSelectBar else null,
+ onManageLogins = {
+ browserAnimator.captureEngineViewAndDrawStatically {
+ val directions =
+ NavGraphDirections.actionGlobalSavedLoginsAuthFragment()
+ findNavController().navigate(directions)
+ }
+ }
+ ),
owner = this,
view = view
)
@@ -486,7 +506,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
feature = SessionFeature(
requireComponents.core.store,
requireComponents.useCases.sessionUseCases.goBack,
- requireComponents.useCases.engineSessionUseCases,
view.engineView,
customTabSessionId
),
@@ -494,6 +513,26 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
view = view
)
+ searchFeature.set(
+ feature = SearchFeature(store, customTabSessionId) { request, tabId ->
+ val parentSession = sessionManager.findSessionById(tabId)
+ val useCase = if (request.isPrivate) {
+ requireComponents.useCases.searchUseCases.newPrivateTabSearch
+ } else {
+ requireComponents.useCases.searchUseCases.newTabSearch
+ }
+
+ if (parentSession?.isCustomTabSession() == true) {
+ useCase.invoke(request.query)
+ requireActivity().startActivity(openInFenixIntent)
+ } else {
+ useCase.invoke(request.query, parentSession = parentSession)
+ }
+ },
+ owner = this,
+ view = view
+ )
+
val accentHighContrastColor =
ThemeManager.resolveAttribute(R.attr.accentHighContrast, context)
@@ -541,7 +580,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
fullScreenFeature.set(
feature = FullScreenFeature(
requireComponents.core.store,
- SessionUseCases(sessionManager),
+ requireComponents.useCases.sessionUseCases,
customTabSessionId,
::viewportFitChange,
::fullScreenChanged
@@ -593,7 +632,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
if (showEngineView) {
engineView?.asView()?.isVisible = true
- swipeRefresh.alpha = 1f
+ swipeRefresh?.alpha = 1f
} else {
engineView?.asView()?.isVisible = false
}
@@ -1063,11 +1102,38 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
*/
override fun onDestroyView() {
super.onDestroyView()
+
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onDestroyView()"
+ )
+
requireContext().accessibilityManager.removeAccessibilityStateChangeListener(this)
_browserToolbarView = null
_browserInteractor = null
}
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onAttach()"
+ )
+ }
+
+ override fun onDetach() {
+ super.onDetach()
+
+ // Diagnostic breadcrumb for "Display already aquired" crash:
+ // https://github.com/mozilla-mobile/android-components/issues/7960
+ breadcrumb(
+ message = "onDetach()"
+ )
+ }
+
companion object {
private const val KEY_CUSTOM_TAB_SESSION_ID = "custom_tab_session_id"
private const val REQUEST_CODE_DOWNLOAD_PERMISSIONS = 1
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 9163d8ab5..913abac35 100644
--- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt
@@ -15,7 +15,6 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
-import kotlinx.android.synthetic.main.browser_gesture_wrapper.*
import kotlinx.android.synthetic.main.fragment_browser.*
import kotlinx.android.synthetic.main.fragment_browser.view.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -26,13 +25,11 @@ import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.feature.app.links.AppLinksUseCases
import mozilla.components.feature.contextmenu.ContextMenuCandidate
import mozilla.components.feature.readerview.ReaderViewFeature
-import mozilla.components.feature.search.SearchFeature
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.tabs.WindowFeature
import mozilla.components.support.base.feature.UserInteractionHandler
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
-import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.addons.runIfFragmentIsAttached
import org.mozilla.fenix.components.FenixSnackbar
@@ -55,8 +52,6 @@ import org.mozilla.fenix.trackingprotection.TrackingProtectionOverlay
class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
private val windowFeature = ViewBoundFeatureWrapper()
- private val searchFeature = ViewBoundFeatureWrapper()
- private val thumbnailsFeature = ViewBoundFeatureWrapper()
private var readerModeAvailable = false
@@ -77,19 +72,15 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
val components = context.components
return super.initializeUI(view)?.also {
- // We need to wrap this whole thing in an if here because gestureLayout will not exist
- // if the feature flag is off
- if (FeatureFlags.browserChromeGestures) {
- gestureLayout.addGestureListener(
- ToolbarGestureHandler(
- activity = requireActivity(),
- contentLayout = browserLayout,
- tabPreview = tabPreview,
- toolbarLayout = browserToolbarView.view,
- sessionManager = components.core.sessionManager
- )
+ gestureLayout.addGestureListener(
+ ToolbarGestureHandler(
+ activity = requireActivity(),
+ contentLayout = browserLayout,
+ tabPreview = tabPreview,
+ toolbarLayout = browserToolbarView.view,
+ sessionManager = components.core.sessionManager
)
- }
+ )
val readerModeAction =
BrowserToolbar.ToggleButton(
@@ -148,23 +139,6 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
owner = this,
view = view
)
- searchFeature.set(
- feature = SearchFeature(components.core.store) {
- if (it.isPrivate) {
- components.useCases.searchUseCases.newPrivateTabSearch.invoke(
- it.query,
- parentSession = getSessionById()
- )
- } else {
- components.useCases.searchUseCases.newTabSearch.invoke(
- it.query,
- parentSession = getSessionById()
- )
- }
- },
- owner = this,
- view = view
- )
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt b/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt
index cd507391b..a7bfe9d4d 100644
--- a/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt
+++ b/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt
@@ -6,19 +6,19 @@ package org.mozilla.fenix.browser
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
import android.app.Activity
import android.graphics.PointF
import android.graphics.Rect
-import android.util.TypedValue
import android.view.View
import android.view.ViewConfiguration
import androidx.annotation.Dimension
import androidx.annotation.Dimension.DP
+import androidx.core.animation.doOnEnd
import androidx.core.graphics.contains
import androidx.core.graphics.toPoint
import androidx.core.view.isVisible
-import androidx.dynamicanimation.animation.DynamicAnimation
-import androidx.dynamicanimation.animation.FlingAnimation
+import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.support.ktx.android.util.dpToPx
@@ -61,11 +61,6 @@ class ToolbarGestureHandler(
private val touchSlop = ViewConfiguration.get(activity).scaledTouchSlop
private val minimumFlingVelocity = ViewConfiguration.get(activity).scaledMinimumFlingVelocity
- private val defaultVelocity = TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- MINIMUM_ANIMATION_VELOCITY,
- activity.resources.displayMetrics
- )
private var gestureDirection = GestureDirection.LEFT_TO_RIGHT
@@ -143,25 +138,12 @@ class ToolbarGestureHandler(
) {
val destination = getDestination()
if (destination is Destination.Tab && isGestureComplete(velocityX)) {
- animateToNextTab(velocityX, destination.session)
+ animateToNextTab(destination.session)
} else {
animateCanceledGesture(velocityX)
}
}
- private fun createFlingAnimation(
- view: View,
- minValue: Float,
- maxValue: Float,
- startVelocity: Float
- ): FlingAnimation =
- FlingAnimation(view, DynamicAnimation.TRANSLATION_X).apply {
- setMinValue(minValue)
- setMaxValue(maxValue)
- setStartVelocity(startVelocity)
- friction = ViewConfiguration.getScrollFriction()
- }
-
private fun getDestination(): Destination {
val isLtr = activity.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
val currentSession = sessionManager.selectedSession ?: return Destination.None
@@ -234,73 +216,59 @@ class ToolbarGestureHandler(
abs(velocityX) >= minimumFlingVelocity)
}
- private fun getVelocityFromFling(velocityX: Float): Float {
- return max(abs(velocityX), defaultVelocity)
+ private fun getAnimator(finalContextX: Float, duration: Long): ValueAnimator {
+ return ValueAnimator.ofFloat(contentLayout.translationX, finalContextX).apply {
+ this.duration = duration
+ this.interpolator = LinearOutSlowInInterpolator()
+ addUpdateListener { animator ->
+ val value = animator.animatedValue as Float
+ contentLayout.translationX = value
+ tabPreview.translationX = when (gestureDirection) {
+ GestureDirection.RIGHT_TO_LEFT -> value + windowWidth + previewOffset
+ GestureDirection.LEFT_TO_RIGHT -> value - windowWidth - previewOffset
+ }
+ }
+ }
}
- private fun animateToNextTab(velocityX: Float, session: Session) {
+ private fun animateToNextTab(session: Session) {
val browserFinalXCoordinate: Float = when (gestureDirection) {
GestureDirection.RIGHT_TO_LEFT -> -windowWidth.toFloat() - previewOffset
GestureDirection.LEFT_TO_RIGHT -> windowWidth.toFloat() + previewOffset
}
- val animationVelocity = when (gestureDirection) {
- GestureDirection.RIGHT_TO_LEFT -> -getVelocityFromFling(velocityX)
- GestureDirection.LEFT_TO_RIGHT -> getVelocityFromFling(velocityX)
- }
// Finish animating the contentLayout off screen and tabPreview on screen
- createFlingAnimation(
- view = contentLayout,
- minValue = min(0f, browserFinalXCoordinate),
- maxValue = max(0f, browserFinalXCoordinate),
- startVelocity = animationVelocity
- ).addUpdateListener { _, value, _ ->
- tabPreview.translationX = when (gestureDirection) {
- GestureDirection.RIGHT_TO_LEFT -> value + windowWidth + previewOffset
- GestureDirection.LEFT_TO_RIGHT -> value - windowWidth - previewOffset
+ getAnimator(browserFinalXCoordinate, FINISHED_GESTURE_ANIMATION_DURATION).apply {
+ doOnEnd {
+ contentLayout.translationX = 0f
+ sessionManager.select(session)
+
+ // Fade out the tab preview to prevent flickering
+ val shortAnimationDuration =
+ activity.resources.getInteger(android.R.integer.config_shortAnimTime)
+ tabPreview.animate()
+ .alpha(0f)
+ .setDuration(shortAnimationDuration.toLong())
+ .setListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ tabPreview.isVisible = false
+ }
+ })
}
- }.addEndListener { _, _, _, _ ->
- contentLayout.translationX = 0f
- sessionManager.select(session)
-
- // Fade out the tab preview to prevent flickering
- val shortAnimationDuration =
- activity.resources.getInteger(android.R.integer.config_shortAnimTime)
- tabPreview.animate()
- .alpha(0f)
- .setDuration(shortAnimationDuration.toLong())
- .setListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- tabPreview.isVisible = false
- }
- })
}.start()
}
- private fun animateCanceledGesture(gestureVelocity: Float) {
- val velocity = if (getDestination() is Destination.None) {
- defaultVelocity
+ private fun animateCanceledGesture(velocityX: Float) {
+ val duration = if (abs(velocityX) >= minimumFlingVelocity) {
+ CANCELED_FLING_ANIMATION_DURATION
} else {
- getVelocityFromFling(gestureVelocity)
- }.let { v ->
- when (gestureDirection) {
- GestureDirection.RIGHT_TO_LEFT -> v
- GestureDirection.LEFT_TO_RIGHT -> -v
- }
+ CANCELED_GESTURE_ANIMATION_DURATION
}
- createFlingAnimation(
- view = contentLayout,
- minValue = min(0f, contentLayout.translationX),
- maxValue = max(0f, contentLayout.translationX),
- startVelocity = velocity
- ).addUpdateListener { _, value, _ ->
- tabPreview.translationX = when (gestureDirection) {
- GestureDirection.RIGHT_TO_LEFT -> value + windowWidth + previewOffset
- GestureDirection.LEFT_TO_RIGHT -> value - windowWidth - previewOffset
+ getAnimator(0f, duration).apply {
+ doOnEnd {
+ tabPreview.isVisible = false
}
- }.addEndListener { _, _, _, _ ->
- tabPreview.isVisible = false
}.start()
}
@@ -337,15 +305,24 @@ class ToolbarGestureHandler(
private const val OVERSCROLL_HIDE_PERCENT = 0.20
/**
- * The speed of the fling animation (in dp per second).
+ * The size of the gap between the tab preview and content layout.
*/
@Dimension(unit = DP)
- private const val MINIMUM_ANIMATION_VELOCITY = 1500f
+ private const val PREVIEW_OFFSET = 48
/**
- * The size of the gap between the tab preview and content layout.
+ * Animation duration when switching to another tab
*/
- @Dimension(unit = DP)
- private const val PREVIEW_OFFSET = 48
+ private const val FINISHED_GESTURE_ANIMATION_DURATION = 250L
+
+ /**
+ * Animation duration gesture is canceled due to the swipe not being far enough
+ */
+ private const val CANCELED_GESTURE_ANIMATION_DURATION = 200L
+
+ /**
+ * Animation duration gesture is canceled due to a swipe in the opposite direction
+ */
+ private const val CANCELED_FLING_ANIMATION_DURATION = 150L
}
}
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 f4b5a7710..44f4131fe 100644
--- a/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt
@@ -32,7 +32,6 @@ import mozilla.components.service.fxa.sync.GlobalSyncableStoreProvider
import mozilla.components.service.sync.logins.SyncableLoginsStorage
import mozilla.components.support.utils.RunWhenReadyQueue
import org.mozilla.fenix.Config
-import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
@@ -85,11 +84,8 @@ class BackgroundServices(
)
@VisibleForTesting
- val supportedEngines = if (FeatureFlags.syncedTabs) {
+ val supportedEngines =
setOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Passwords, SyncEngine.Tabs)
- } else {
- setOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Passwords)
- }
private val syncConfig = SyncConfig(supportedEngines, syncPeriodInMinutes = 240L) // four hours
init {
@@ -98,10 +94,7 @@ class BackgroundServices(
GlobalSyncableStoreProvider.configureStore(SyncEngine.History to historyStorage)
GlobalSyncableStoreProvider.configureStore(SyncEngine.Bookmarks to bookmarkStorage)
GlobalSyncableStoreProvider.configureStore(SyncEngine.Passwords to passwordsStorage)
-
- if (FeatureFlags.syncedTabs) {
- GlobalSyncableStoreProvider.configureStore(SyncEngine.Tabs to remoteTabsStorage)
- }
+ GlobalSyncableStoreProvider.configureStore(SyncEngine.Tabs to remoteTabsStorage)
}
private val telemetryAccountObserver = TelemetryAccountObserver(
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 8528844b7..9e2b069f5 100644
--- a/app/src/main/java/org/mozilla/fenix/components/Components.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/Components.kt
@@ -18,7 +18,7 @@ import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.support.migration.state.MigrationStore
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.HomeActivity
-import org.mozilla.fenix.components.metrics.AppAllSourceStartTelemetry
+import org.mozilla.fenix.components.metrics.AppStartupTelemetry
import org.mozilla.fenix.utils.ClipboardHandler
import org.mozilla.fenix.utils.Mockable
import org.mozilla.fenix.utils.Settings
@@ -44,7 +44,7 @@ class Components(private val context: Context) {
)
}
val services by lazy { Services(context, backgroundServices.accountManager) }
- val core by lazy { Core(context) }
+ val core by lazy { Core(context, analytics.crashReporter) }
val search by lazy { Search(context) }
val useCases by lazy {
UseCases(
@@ -53,7 +53,8 @@ class Components(private val context: Context) {
core.sessionManager,
core.store,
search.searchEngineManager,
- core.webAppShortcutManager
+ core.webAppShortcutManager,
+ core.topSiteStorage
)
}
val intentProcessors by lazy {
@@ -83,7 +84,7 @@ class Components(private val context: Context) {
}
}
- val appAllSourceStartTelemetry by lazy { AppAllSourceStartTelemetry(analytics.metrics) }
+ val appStartupTelemetry by lazy { AppStartupTelemetry(analytics.metrics) }
@Suppress("MagicNumber")
val addonUpdater by lazy {
@@ -114,4 +115,11 @@ class Components(private val context: Context) {
val wifiConnectionMonitor by lazy { WifiConnectionMonitor(context.getSystemService()!!) }
val settings by lazy { Settings(context) }
+
+ val reviewPromptController by lazy {
+ ReviewPromptController(
+ context,
+ FenixReviewSettings(settings)
+ )
+ }
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/Core.kt b/app/src/main/java/org/mozilla/fenix/components/Core.kt
index 8df2e3f56..3b0a5bdb8 100644
--- a/app/src/main/java/org/mozilla/fenix/components/Core.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/Core.kt
@@ -7,6 +7,7 @@ package org.mozilla.fenix.components
import GeckoProvider
import android.content.Context
import android.content.res.Configuration
+import android.os.StrictMode
import io.sentry.Sentry
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
@@ -15,7 +16,9 @@ import kotlinx.coroutines.withContext
import mozilla.components.browser.engine.gecko.GeckoEngine
import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient
import mozilla.components.browser.icons.BrowserIcons
+import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
+import mozilla.components.browser.session.engine.EngineMiddleware
import mozilla.components.browser.session.storage.SessionStorage
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.store.BrowserStore
@@ -37,6 +40,8 @@ import mozilla.components.feature.pwa.ManifestStorage
import mozilla.components.feature.pwa.WebAppShortcutManager
import mozilla.components.feature.readerview.ReaderViewMiddleware
import mozilla.components.feature.session.HistoryDelegate
+import mozilla.components.feature.top.sites.DefaultTopSitesStorage
+import mozilla.components.feature.top.sites.PinnedSiteStorage
import mozilla.components.feature.webcompat.WebCompatFeature
import mozilla.components.feature.webcompat.reporter.WebCompatReporterFeature
import mozilla.components.feature.webnotifications.WebNotificationFeature
@@ -46,16 +51,21 @@ import mozilla.components.service.digitalassetlinks.RelationChecker
import mozilla.components.service.digitalassetlinks.local.StatementApi
import mozilla.components.service.digitalassetlinks.local.StatementRelationChecker
import mozilla.components.service.sync.logins.SyncableLoginsStorage
+import mozilla.components.support.base.crash.CrashReporting
+import mozilla.components.support.locale.LocaleManager
import org.mozilla.fenix.AppRequestInterceptor
import org.mozilla.fenix.Config
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.downloads.DownloadService
import org.mozilla.fenix.ext.components
+import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.media.MediaService
import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry
import org.mozilla.fenix.search.telemetry.incontent.InContentTelemetry
+import org.mozilla.fenix.settings.SupportUtils
+import org.mozilla.fenix.settings.advanced.getSelectedLocale
import org.mozilla.fenix.utils.Mockable
import java.util.concurrent.TimeUnit
@@ -63,7 +73,7 @@ import java.util.concurrent.TimeUnit
* Component group for all core browser functionality.
*/
@Mockable
-class Core(private val context: Context) {
+class Core(private val context: Context, private val crashReporter: CrashReporting) {
/**
* The browser engine component initialized based on the build
* configuration (see build variants).
@@ -134,10 +144,14 @@ class Core(private val context: Context) {
DownloadMiddleware(context, DownloadService::class.java),
ReaderViewMiddleware(),
ThumbnailsMiddleware(thumbnailStorage)
- )
+ ) + EngineMiddleware.create(engine, ::findSessionById)
)
}
+ private fun findSessionById(tabId: String): Session? {
+ return sessionManager.findSessionById(tabId)
+ }
+
/**
* The [CustomTabsServiceStore] holds global custom tabs related data.
*/
@@ -184,7 +198,7 @@ class Core(private val context: Context) {
// Now that we have restored our previous state (if there's one) let's setup auto saving the state while
// the app is used.
- sessionStorage.autoSave(sessionManager)
+ sessionStorage.autoSave(store)
.periodicallyInForeground(interval = 30, unit = TimeUnit.SECONDS)
.whenGoingToBackground()
.whenSessionsChange()
@@ -228,7 +242,7 @@ class Core(private val context: Context) {
// Use these for startup-path code, where we don't want to do any work that's not strictly necessary.
// For example, this is how the GeckoEngine delegates (history, logins) are configured.
// We can fully initialize GeckoEngine without initialized our storage.
- val lazyHistoryStorage = lazy { PlacesHistoryStorage(context) }
+ val lazyHistoryStorage = lazy { PlacesHistoryStorage(context, crashReporter) }
val lazyBookmarksStorage = lazy { PlacesBookmarksStorage(context) }
val lazyPasswordsStorage = lazy { SyncableLoginsStorage(context, passwordsEncryptionKey) }
@@ -249,7 +263,46 @@ class Core(private val context: Context) {
*/
val thumbnailStorage by lazy { ThumbnailStorage(context) }
- val topSiteStorage by lazy { TopSiteStorage(context) }
+ val pinnedSiteStorage by lazy { PinnedSiteStorage(context) }
+
+ val topSiteStorage by lazy {
+ val defaultTopSites = mutableListOf>()
+
+ StrictMode.allowThreadDiskReads().resetPoliciesAfter {
+ if (!context.settings().defaultTopSitesAdded) {
+ defaultTopSites.add(
+ Pair(
+ context.getString(R.string.default_top_site_google),
+ SupportUtils.GOOGLE_URL
+ )
+ )
+
+ if (LocaleManager.getSelectedLocale(context).language == "en") {
+ defaultTopSites.add(
+ Pair(
+ context.getString(R.string.pocket_pinned_top_articles),
+ SupportUtils.POCKET_TRENDING_URL
+ )
+ )
+ }
+
+ defaultTopSites.add(
+ Pair(
+ context.getString(R.string.default_top_site_wikipedia),
+ SupportUtils.WIKIPEDIA_URL
+ )
+ )
+
+ context.settings().defaultTopSitesAdded = true
+ }
+ }
+
+ DefaultTopSitesStorage(
+ pinnedSiteStorage,
+ historyStorage,
+ defaultTopSites
+ )
+ }
val permissionStorage by lazy { PermissionStorage(context) }
@@ -273,7 +326,8 @@ class Core(private val context: Context) {
getSecureAbove22Preferences().getString(PASSWORDS_KEY)
?: generateEncryptionKey(KEY_STRENGTH).also {
if (context.settings().passwordsEncryptionKeyGenerated &&
- isSentryEnabled()) {
+ isSentryEnabled()
+ ) {
// We already had previously generated an encryption key, but we have lost it
Sentry.capture("Passwords encryption key for passwords storage was lost and we generated a new one")
}
@@ -290,7 +344,7 @@ class Core(private val context: Context) {
fun getPreferredColorScheme(): PreferredColorScheme {
val inDark =
(context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
- Configuration.UI_MODE_NIGHT_YES
+ Configuration.UI_MODE_NIGHT_YES
return when {
context.settings().shouldUseDarkTheme -> PreferredColorScheme.Dark
context.settings().shouldUseLightTheme -> PreferredColorScheme.Light
diff --git a/app/src/main/java/org/mozilla/fenix/components/FenixSnackbar.kt b/app/src/main/java/org/mozilla/fenix/components/FenixSnackbar.kt
index e8804e385..bd7a4c569 100644
--- a/app/src/main/java/org/mozilla/fenix/components/FenixSnackbar.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/FenixSnackbar.kt
@@ -12,6 +12,7 @@ import android.widget.FrameLayout
import androidx.appcompat.content.res.AppCompatResources
import androidx.appcompat.widget.ContentFrameLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
+import androidx.core.view.updatePadding
import androidx.core.widget.TextViewCompat
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.ContentViewCallback
@@ -124,11 +125,8 @@ class FenixSnackbar private constructor(
return FenixSnackbar(parent, content, callback, isError).also {
it.duration = durationOrAccessibleDuration
- it.view.setPadding(
- 0,
- 0,
- 0,
- if (
+ it.view.updatePadding(
+ bottom = if (
isDisplayedWithBrowserToolbar &&
shouldUseBottomToolbar &&
// If the view passed in is a ContentFrameLayout, it does not matter
diff --git a/app/src/main/java/org/mozilla/fenix/components/PerformanceComponent.kt b/app/src/main/java/org/mozilla/fenix/components/PerformanceComponent.kt
index 615d348a8..b692d216b 100644
--- a/app/src/main/java/org/mozilla/fenix/components/PerformanceComponent.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/PerformanceComponent.kt
@@ -5,10 +5,11 @@
package org.mozilla.fenix.components
import mozilla.components.support.utils.RunWhenReadyQueue
+import org.mozilla.fenix.perf.VisualCompletenessQueue
/**
* Component group for all functionality related to performance.
*/
class PerformanceComponent {
- val visualCompletenessQueue by lazy { RunWhenReadyQueue() }
+ val visualCompletenessQueue by lazy { VisualCompletenessQueue(RunWhenReadyQueue()) }
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/ReviewPromptController.kt b/app/src/main/java/org/mozilla/fenix/components/ReviewPromptController.kt
new file mode 100644
index 000000000..b7f81f361
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/components/ReviewPromptController.kt
@@ -0,0 +1,99 @@
+/* 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.components
+
+import android.app.Activity
+import android.content.Context
+import androidx.annotation.VisibleForTesting
+import com.google.android.play.core.ktx.launchReview
+import com.google.android.play.core.ktx.requestReview
+import com.google.android.play.core.review.ReviewManagerFactory
+import kotlinx.coroutines.Dispatchers.Main
+import kotlinx.coroutines.withContext
+import org.mozilla.fenix.utils.Settings
+
+/**
+ * Interface that describes the settings needed to track the Review Prompt.
+ */
+interface ReviewSettings {
+ var numberOfAppLaunches: Int
+ val isDefaultBrowser: Boolean
+ var lastReviewPromptTimeInMillis: Long
+}
+
+/**
+ * Wraps `Settings` to conform to `ReviewSettings`.
+ */
+class FenixReviewSettings(
+ val settings: Settings
+) : ReviewSettings {
+ override var numberOfAppLaunches: Int
+ get() = settings.numberOfAppLaunches
+ set(value) { settings.numberOfAppLaunches = value }
+ override val isDefaultBrowser: Boolean
+ get() = settings.isDefaultBrowser()
+ override var lastReviewPromptTimeInMillis: Long
+ get() = settings.lastReviewPromptTimeInMillis
+ set(value) { settings.lastReviewPromptTimeInMillis = value }
+}
+
+/**
+ * Controls the Review Prompt behavior.
+ */
+class ReviewPromptController(
+ private val context: Context,
+ private val reviewSettings: ReviewSettings,
+ private val timeNowInMillis: () -> Long = { System.currentTimeMillis() },
+ private val tryPromptReview: suspend (Activity) -> Unit = {
+ val manager = ReviewManagerFactory.create(context)
+ val reviewInfo = manager.requestReview()
+
+ withContext(Main) {
+ manager.launchReview(it, reviewInfo)
+ }
+ }
+) {
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ @Volatile var reviewPromptIsReady = false
+
+ suspend fun promptReview(activity: Activity) {
+ if (shouldShowPrompt()) {
+ tryPromptReview(activity)
+ reviewSettings.lastReviewPromptTimeInMillis = timeNowInMillis()
+ }
+ }
+
+ fun trackApplicationLaunch() {
+ reviewSettings.numberOfAppLaunches = reviewSettings.numberOfAppLaunches + 1
+ // We only want to show the the prompt after we've finished "launching" the application.
+ reviewPromptIsReady = true
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ fun shouldShowPrompt(): Boolean {
+ if (!reviewPromptIsReady) {
+ return false
+ } else {
+ // We only want to try to show it once to avoid unnecessary disk reads
+ reviewPromptIsReady = false
+ }
+
+ if (!reviewSettings.isDefaultBrowser) { return false }
+
+ val hasOpenedFiveTimes = reviewSettings.numberOfAppLaunches >= NUMBER_OF_LAUNCHES_REQUIRED
+ val now = timeNowInMillis()
+ val apprxFourMonthsAgo = now - (APPRX_MONTH_IN_MILLIS * NUMBER_OF_MONTHS_TO_PASS)
+ val lastPrompt = reviewSettings.lastReviewPromptTimeInMillis
+ val hasNotBeenPromptedLastFourMonths = lastPrompt == 0L || lastPrompt <= apprxFourMonthsAgo
+
+ return hasOpenedFiveTimes && hasNotBeenPromptedLastFourMonths
+ }
+
+ companion object {
+ private const val APPRX_MONTH_IN_MILLIS: Long = 1000L * 60L * 60L * 24L * 30L
+ private const val NUMBER_OF_LAUNCHES_REQUIRED = 5
+ private const val NUMBER_OF_MONTHS_TO_PASS = 4
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/components/TopSiteStorage.kt b/app/src/main/java/org/mozilla/fenix/components/TopSiteStorage.kt
deleted file mode 100644
index c7c0347bd..000000000
--- a/app/src/main/java/org/mozilla/fenix/components/TopSiteStorage.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.fenix.components
-
-import android.content.Context
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.asLiveData
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
-import mozilla.components.feature.top.sites.TopSite
-import mozilla.components.feature.top.sites.TopSiteStorage
-import mozilla.components.support.locale.LocaleManager
-import org.mozilla.fenix.R
-import org.mozilla.fenix.ext.observeOnce
-import org.mozilla.fenix.ext.settings
-import org.mozilla.fenix.settings.SupportUtils
-import org.mozilla.fenix.settings.advanced.getSelectedLocale
-import org.mozilla.fenix.utils.Mockable
-
-@Mockable
-class TopSiteStorage(private val context: Context) {
- var cachedTopSites = listOf()
-
- val storage by lazy {
- TopSiteStorage(context)
- }
-
- init {
- addDefaultTopSites()
- }
-
- /**
- * Adds a new [TopSite].
- */
- fun addTopSite(title: String, url: String, isDefault: Boolean = false) {
- storage.addTopSite(title, url, isDefault)
- }
-
- /**
- * Returns a [LiveData] list of all the [TopSite] instances.
- */
- fun getTopSites(): LiveData> {
- return storage.getTopSites().asLiveData()
- }
-
- /**
- * Removes the given [TopSite].
- */
- fun removeTopSite(topSite: TopSite) {
- storage.removeTopSite(topSite)
- }
-
- private fun addDefaultTopSites() {
- val topSiteCandidates = mutableListOf>()
- if (!context.settings().defaultTopSitesAdded) {
- topSiteCandidates.add(
- Pair(
- context.getString(R.string.default_top_site_google),
- SupportUtils.GOOGLE_URL
- )
- )
-
- if (LocaleManager.getSelectedLocale(context).language == "en") {
- topSiteCandidates.add(
- Pair(
- context.getString(R.string.pocket_pinned_top_articles),
- SupportUtils.POCKET_TRENDING_URL
- )
- )
- }
-
- topSiteCandidates.add(
- Pair(
- context.getString(R.string.default_top_site_wikipedia),
- SupportUtils.WIKIPEDIA_URL
- )
- )
-
- GlobalScope.launch(Dispatchers.IO) {
- topSiteCandidates.forEach { (title, url) ->
- addTopSite(title, url, isDefault = true)
- }
- }
- context.settings().defaultTopSitesAdded = true
- }
- }
-
- fun prefetch() {
- getTopSites().observeOnce {
- cachedTopSites = it
- }
- }
-}
diff --git a/app/src/main/java/org/mozilla/fenix/components/UseCases.kt b/app/src/main/java/org/mozilla/fenix/components/UseCases.kt
index 2cec0c741..9c05a060e 100644
--- a/app/src/main/java/org/mozilla/fenix/components/UseCases.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/UseCases.kt
@@ -7,7 +7,6 @@ package org.mozilla.fenix.components
import android.content.Context
import mozilla.components.browser.search.SearchEngineManager
import mozilla.components.browser.session.SessionManager
-import mozilla.components.browser.session.usecases.EngineSessionUseCases
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.Engine
import mozilla.components.feature.app.links.AppLinksUseCases
@@ -20,6 +19,8 @@ import mozilla.components.feature.session.SessionUseCases
import mozilla.components.feature.session.SettingsUseCases
import mozilla.components.feature.session.TrackingProtectionUseCases
import mozilla.components.feature.tabs.TabsUseCases
+import mozilla.components.feature.top.sites.TopSitesStorage
+import mozilla.components.feature.top.sites.TopSitesUseCases
import org.mozilla.fenix.utils.Mockable
/**
@@ -34,12 +35,13 @@ class UseCases(
private val sessionManager: SessionManager,
private val store: BrowserStore,
private val searchEngineManager: SearchEngineManager,
- private val shortcutManager: WebAppShortcutManager
+ private val shortcutManager: WebAppShortcutManager,
+ private val topSitesStorage: TopSitesStorage
) {
/**
* Use cases that provide engine interactions for a given browser session.
*/
- val sessionUseCases by lazy { SessionUseCases(sessionManager) }
+ val sessionUseCases by lazy { SessionUseCases(store, sessionManager) }
/**
* Use cases that provide tab management.
@@ -49,7 +51,7 @@ class UseCases(
/**
* Use cases that provide search engine integration.
*/
- val searchUseCases by lazy { SearchUseCases(context, searchEngineManager, sessionManager) }
+ val searchUseCases by lazy { SearchUseCases(context, store, searchEngineManager, sessionManager) }
/**
* Use cases that provide settings management.
@@ -66,7 +68,10 @@ class UseCases(
val contextMenuUseCases by lazy { ContextMenuUseCases(store) }
- val engineSessionUseCases by lazy { EngineSessionUseCases(sessionManager) }
-
val trackingProtectionUseCases by lazy { TrackingProtectionUseCases(store, engine) }
+
+ /**
+ * Use cases that provide top sites management.
+ */
+ val topSitesUseCase by lazy { TopSitesUseCases(topSitesStorage) }
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/AppAllSourceStartTelemetry.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/AppAllSourceStartTelemetry.kt
deleted file mode 100644
index ce730d4f1..000000000
--- a/app/src/main/java/org/mozilla/fenix/components/metrics/AppAllSourceStartTelemetry.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.fenix.components.metrics
-
-import android.content.Intent
-import androidx.annotation.VisibleForTesting
-import androidx.annotation.VisibleForTesting.PRIVATE
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleObserver
-import androidx.lifecycle.OnLifecycleEvent
-import androidx.lifecycle.ProcessLifecycleOwner
-import mozilla.components.support.utils.SafeIntent
-
-/**
- * Tracks how the application was opened through [Event.AppOpenedAllSourceStartup].
- * We only considered to be "opened" if it received an intent and the app was in the background.
- */
-class AppAllSourceStartTelemetry(private val metrics: MetricController) : LifecycleObserver {
-
- // default value is true to capture the first launch of the application
- private var wasApplicationInBackground = true
-
- init {
- ProcessLifecycleOwner.get().lifecycle.addObserver(this)
- }
-
- fun receivedIntentInExternalAppBrowserActivity(safeIntent: SafeIntent) {
- setAppOpenedAllSourceFromIntent(safeIntent, true)
- }
-
- fun receivedIntentInHomeActivity(safeIntent: SafeIntent) {
- setAppOpenedAllSourceFromIntent(safeIntent, false)
- }
-
- private fun setAppOpenedAllSourceFromIntent(intent: SafeIntent, isExternalAppBrowserActivity: Boolean) {
- if (!wasApplicationInBackground) {
- return
- }
-
- val source = when {
- isExternalAppBrowserActivity -> Event.AppOpenedAllSourceStartup.Source.CUSTOM_TAB
- intent.isLauncherIntent -> Event.AppOpenedAllSourceStartup.Source.APP_ICON
- intent.action == Intent.ACTION_VIEW -> Event.AppOpenedAllSourceStartup.Source.LINK
- else -> Event.AppOpenedAllSourceStartup.Source.UNKNOWN
- }
-
- metrics.track(Event.AppOpenedAllSourceStartup(source))
-
- wasApplicationInBackground = false
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
- @VisibleForTesting(otherwise = PRIVATE)
- fun onApplicationOnStop() {
- wasApplicationInBackground = true
- }
-}
diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/AppStartupTelemetry.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/AppStartupTelemetry.kt
new file mode 100644
index 000000000..d5ab320cc
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/components/metrics/AppStartupTelemetry.kt
@@ -0,0 +1,146 @@
+/* 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.components.metrics
+
+import android.content.Intent
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.OnLifecycleEvent
+import androidx.lifecycle.ProcessLifecycleOwner
+import mozilla.components.support.utils.SafeIntent
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.APP_ICON
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.CUSTOM_TAB
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.LINK
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.UNKNOWN
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.ERROR
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.HOT
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.COLD
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.WARM
+
+/**
+ * Tracks application startup source, type, and whether or not activity has savedInstance to restore
+ * the activity from. Sample metric = [source = COLD, type = APP_ICON, hasSavedInstance = false]
+ * The basic idea is to collect these metrics from different phases of startup through
+ * [AppAllStartup] and finally report them on Activity's onResume() function.
+ */
+@Suppress("TooManyFunctions")
+class AppStartupTelemetry(private val metrics: MetricController) : LifecycleObserver {
+
+ init {
+ ProcessLifecycleOwner.get().lifecycle.addObserver(this)
+ }
+
+ private var isMetricRecordedSinceAppWasForegrounded = false
+ private var wasAppCreateCalledBeforeActivityCreate = false
+
+ private var onCreateData: AppAllStartup? = null
+ private var onRestartData: Pair? = null
+ private var onNewIntentData: Source? = null
+
+ fun onFenixApplicationOnCreate() {
+ wasAppCreateCalledBeforeActivityCreate = true
+ }
+
+ fun onHomeActivityOnCreate(safeIntent: SafeIntent, hasSavedInstanceState: Boolean) {
+ setOnCreateData(safeIntent, hasSavedInstanceState, false)
+ }
+
+ fun onExternalAppBrowserOnCreate(safeIntent: SafeIntent, hasSavedInstanceState: Boolean) {
+ setOnCreateData(safeIntent, hasSavedInstanceState, true)
+ }
+
+ fun onHomeActivityOnRestart() {
+ // we are not setting [Source] in this method since source is derived from an intent.
+ // therefore source gets set in onNewIntent().
+ onRestartData = Pair(HOT, null)
+ }
+
+ fun onHomeActivityOnNewIntent(safeIntent: SafeIntent) {
+ // we are only setting [Source] in this method since source is derived from an intent].
+ // other metric fields are set in onRestart()
+ onNewIntentData = getStartupSourceFromIntent(safeIntent, false)
+ }
+
+ private fun setOnCreateData(
+ safeIntent: SafeIntent,
+ hasSavedInstanceState: Boolean,
+ isExternalAppBrowserActivity: Boolean
+ ) {
+ onCreateData = AppAllStartup(
+ getStartupSourceFromIntent(safeIntent, isExternalAppBrowserActivity),
+ getAppStartupType(),
+ hasSavedInstanceState
+ )
+ wasAppCreateCalledBeforeActivityCreate = false
+ }
+
+ private fun getAppStartupType(): Type {
+ return if (wasAppCreateCalledBeforeActivityCreate) COLD else WARM
+ }
+
+ private fun getStartupSourceFromIntent(
+ intent: SafeIntent,
+ isExternalAppBrowserActivity: Boolean
+ ): Source {
+ return when {
+ // since the intent action is same (ACTION_VIEW) for both CUSTOM_TAB and LINK.
+ // we have to make sure that we are checking for CUSTOM_TAB condition first as this
+ // check does not rely on intent action
+ isExternalAppBrowserActivity -> CUSTOM_TAB
+ intent.isLauncherIntent -> APP_ICON
+ intent.action == Intent.ACTION_VIEW -> LINK
+ // one of the unknown case is app switcher, where we go to the recent tasks to launch
+ // Fenix.
+ else -> UNKNOWN
+ }
+ }
+
+ /**
+ * The reason we record metric on resume is because we need to wait for onNewIntent(), and
+ * we are not guaranteed that onNewIntent() will be called before or after onStart() / onRestart().
+ * However we are guaranteed onResume() will be called after onNewIntent() and onStart(). Source:
+ * https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent)
+ */
+ fun onHomeActivityOnResume() {
+ recordMetric()
+ }
+
+ private fun recordMetric() {
+ if (!isMetricRecordedSinceAppWasForegrounded) {
+ val appAllStartup: AppAllStartup = if (onCreateData != null) {
+ onCreateData!!
+ } else {
+ mergeOnRestartAndOnNewIntentIntoStartup()
+ }
+ metrics.track(appAllStartup)
+ isMetricRecordedSinceAppWasForegrounded = true
+ }
+ // we don't want any weird previous states to persist on our next metric record.
+ onCreateData = null
+ onNewIntentData = null
+ onRestartData = null
+ }
+
+ private fun mergeOnRestartAndOnNewIntentIntoStartup(): AppAllStartup {
+ return AppAllStartup(
+ onNewIntentData ?: UNKNOWN,
+ onRestartData?.first ?: ERROR,
+ onRestartData?.second
+ )
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ fun onApplicationOnStop() {
+ // application was backgrounded, we need to record the new metric type if
+ // application was to come to foreground again.
+ // Therefore we set the isMetricRecorded flag to false.
+ isMetricRecordedSinceAppWasForegrounded = false
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt
index 83d5ef93d..1ca03a57c 100644
--- a/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt
@@ -142,9 +142,6 @@ sealed class Event {
object WhatsNewTapped : Event()
object SupportTapped : Event()
object PrivacyNoticeTapped : Event()
- object RightsTapped : Event()
- object LicensingTapped : Event()
- object LibrariesThatWeUseTapped : Event()
object PocketTopSiteClicked : Event()
object PocketTopSiteRemoved : Event()
object FennecToFenixMigrated : Event()
@@ -319,11 +316,28 @@ sealed class Event {
get() = hashMapOf(Events.appOpenedAllStartupKeys.source to source.name)
}
- data class AppOpenedAllSourceStartup(val source: Source) : Event() {
+ data class AppAllStartup(
+ val source: Source,
+ val type: Type,
+ val hasSavedInstanceState: Boolean? = null
+ ) : Event() {
enum class Source { APP_ICON, LINK, CUSTOM_TAB, UNKNOWN }
+ enum class Type { COLD, WARM, HOT, ERROR }
override val extras: Map?
- get() = hashMapOf(Events.appOpenedAllStartupKeys.source to source.name)
+ get() {
+ val extrasMap = hashMapOf(
+ Events.appOpenedAllStartupKeys.source to source.toString(),
+ Events.appOpenedAllStartupKeys.type to type.toString()
+ )
+ // we are only sending hasSavedInstanceState whenever we get data from
+ // activity's oncreate() method.
+ if (hasSavedInstanceState != null) {
+ extrasMap[Events.appOpenedAllStartupKeys.hasSavedInstanceState] =
+ hasSavedInstanceState.toString()
+ }
+ return extrasMap
+ }
}
data class CollectionSaveButtonPressed(val fromScreen: String) : Event() {
@@ -488,7 +502,7 @@ sealed class Event {
NEW_PRIVATE_TAB, SHARE, BACK, FORWARD, RELOAD, STOP, OPEN_IN_FENIX,
SAVE_TO_COLLECTION, ADD_TO_TOP_SITES, ADD_TO_HOMESCREEN, QUIT, READER_MODE_ON,
READER_MODE_OFF, OPEN_IN_APP, BOOKMARK, READER_MODE_APPEARANCE, ADDONS_MANAGER,
- BOOKMARKS, HISTORY, SYNC_TABS
+ BOOKMARKS, HISTORY, SYNC_TABS, DOWNLOADS
}
override val extras: Map?
diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt
index db2e02180..a89499fce 100644
--- a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt
@@ -106,7 +106,7 @@ private val Event.wrapper: EventWrapper<*>?
{ Events.appReceivedIntent.record(it) },
{ Events.appReceivedIntentKeys.valueOf(it) }
)
- is Event.AppOpenedAllSourceStartup -> EventWrapper(
+ is Event.AppAllStartup -> EventWrapper(
{ Events.appOpenedAllStartup.record(it) },
{ Events.appOpenedAllStartupKeys.valueOf(it) }
)
@@ -531,15 +531,6 @@ private val Event.wrapper: EventWrapper<*>?
is Event.PrivacyNoticeTapped -> EventWrapper(
{ AboutPage.privacyNoticeTapped.record(it) }
)
- is Event.RightsTapped -> EventWrapper(
- { AboutPage.rightsTapped.record(it) }
- )
- is Event.LicensingTapped -> EventWrapper(
- { AboutPage.licensingTapped.record(it) }
- )
- is Event.LibrariesThatWeUseTapped -> EventWrapper(
- { AboutPage.librariesTapped.record(it) }
- )
is Event.PocketTopSiteClicked -> EventWrapper(
{ Pocket.pocketTopSiteClicked.record(it) }
)
@@ -725,7 +716,7 @@ class GleanMetricsService(private val context: Context) : MetricsService {
// The code below doesn't need to execute immediately, so we'll add them to the visual
// completeness task queue to be run later.
- context.components.performance.visualCompletenessQueue.runIfReadyOrQueue {
+ context.components.performance.visualCompletenessQueue.queue.runIfReadyOrQueue {
// We have to initialize Glean *on* the main thread, because it registers lifecycle
// observers. However, the activation ping must be sent *off* of the main thread,
// because it calls Google ad APIs that must be called *off* of the main thread.
diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/LeanplumMetricsService.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/LeanplumMetricsService.kt
index d6b4737f6..acd4dae51 100644
--- a/app/src/main/java/org/mozilla/fenix/components/metrics/LeanplumMetricsService.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/metrics/LeanplumMetricsService.kt
@@ -7,6 +7,7 @@ package org.mozilla.fenix.components.metrics
import android.app.Application
import android.content.Context.MODE_PRIVATE
import android.net.Uri
+import android.os.StrictMode
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.leanplum.Leanplum
@@ -22,6 +23,7 @@ import kotlinx.coroutines.withContext
import mozilla.components.support.locale.LocaleManager
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.components.metrics.MozillaProductDetector.MozillaProducts
+import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.intent.DeepLinkIntentProcessor
import java.util.Locale
@@ -81,7 +83,9 @@ class LeanplumMetricsService(
override val type = MetricServiceType.Marketing
private val token = Token(LeanplumId, LeanplumToken)
- private val preferences = application.getSharedPreferences(PREFERENCE_NAME, MODE_PRIVATE)
+ private val preferences = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
+ application.getSharedPreferences(PREFERENCE_NAME, MODE_PRIVATE)
+ }
@VisibleForTesting
internal val deviceId by lazy {
diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt
index 1694d89f0..6f2234948 100644
--- a/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/metrics/MetricController.kt
@@ -5,6 +5,7 @@
package org.mozilla.fenix.components.metrics
import androidx.annotation.VisibleForTesting
+import com.leanplum.Leanplum
import mozilla.components.browser.awesomebar.facts.BrowserAwesomeBarFacts
import mozilla.components.browser.menu.facts.BrowserMenuFacts
import mozilla.components.browser.toolbar.facts.ToolbarFacts
@@ -193,6 +194,7 @@ internal class ReleaseMetricController(
if (installedAddons is List<*>) {
Addons.installedAddons.set(installedAddons.map { it.toString() })
Addons.hasInstalledAddons.set(installedAddons.size > 0)
+ Leanplum.setUserAttributes(mapOf("installed_addons" to installedAddons.size))
}
}
@@ -200,6 +202,7 @@ internal class ReleaseMetricController(
if (enabledAddons is List<*>) {
Addons.enabledAddons.set(enabledAddons.map { it.toString() })
Addons.hasEnabledAddons.set(enabledAddons.size > 0)
+ Leanplum.setUserAttributes(mapOf("enabled_addons" to enabledAddons.size))
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt
index 5fd5b8ec6..9e0896ec6 100644
--- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt
@@ -23,6 +23,7 @@ import mozilla.components.concept.engine.prompt.ShareData
import mozilla.components.feature.session.SessionFeature
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import mozilla.components.support.ktx.kotlin.isUrl
+import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.R
@@ -34,7 +35,6 @@ import org.mozilla.fenix.browser.readermode.ReaderModeController
import org.mozilla.fenix.collections.SaveCollectionStep
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.TabCollectionStorage
-import org.mozilla.fenix.components.TopSiteStorage
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getRootView
@@ -75,13 +75,12 @@ class DefaultBrowserToolbarController(
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
) : BrowserToolbarController {
private val useNewSearchExperience
- get() = activity.settings().useNewSearchExperience
+ get() = FeatureFlags.newSearchExperience
private val currentSession
get() = customTabSession ?: activity.components.core.sessionManager.selectedSession
@@ -245,7 +244,9 @@ class DefaultBrowserToolbarController(
scope.launch {
ioScope.launch {
currentSession?.let {
- topSiteStorage.addTopSite(it.title, it.url)
+ with(activity.components.useCases.topSitesUseCase) {
+ addPinnedSites(it.title, it.url)
+ }
}
}.join()
@@ -380,6 +381,13 @@ class DefaultBrowserToolbarController(
BrowserFragmentDirections.actionGlobalHistoryFragment()
)
}
+
+ ToolbarMenu.Item.Downloads -> browserAnimator.captureEngineViewAndDrawStatically {
+ navController.nav(
+ R.id.browserFragment,
+ BrowserFragmentDirections.actionGlobalDownloadsFragment()
+ )
+ }
}
}
@@ -414,6 +422,7 @@ class DefaultBrowserToolbarController(
ToolbarMenu.Item.AddonsManager -> Event.BrowserMenuItemTapped.Item.ADDONS_MANAGER
ToolbarMenu.Item.Bookmarks -> Event.BrowserMenuItemTapped.Item.BOOKMARKS
ToolbarMenu.Item.History -> Event.BrowserMenuItemTapped.Item.HISTORY
+ ToolbarMenu.Item.Downloads -> Event.BrowserMenuItemTapped.Item.DOWNLOADS
}
activity.components.analytics.metrics.track(Event.BrowserMenuItemTapped(eventItem))
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 dc98afa23..ff549ffa2 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
@@ -177,11 +177,14 @@ class DefaultToolbarMenu(
?.browsingModeManager?.mode == BrowsingMode.Normal
val shouldDeleteDataOnQuit = context.components.settings
.shouldDeleteBrowsingDataOnQuit
+ val syncedTabsInTabsTray = context.components.settings
+ .syncedTabsInTabsTray
val menuItems = listOfNotNull(
+ if (FeatureFlags.viewDownloads) downloadsItem else null,
historyItem,
bookmarksItem,
- if (FeatureFlags.syncedTabs) syncedTabs else null,
+ if (syncedTabsInTabsTray) null else syncedTabs,
settings,
if (shouldDeleteDataOnQuit) deleteDataOnQuit else null,
BrowserMenuDivider(),
@@ -333,6 +336,14 @@ class DefaultToolbarMenu(
onItemTapped.invoke(ToolbarMenu.Item.Bookmarks)
}
+ val downloadsItem = BrowserMenuImageText(
+ "Downloads",
+ R.drawable.ic_download,
+ primaryTextColor()
+ ) {
+ onItemTapped.invoke(ToolbarMenu.Item.Downloads)
+ }
+
@ColorRes
private fun primaryTextColor() = ThemeManager.resolveAttribute(R.attr.primaryText, context)
diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounter.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounter.kt
index 3677d6cde..fe015efa0 100644
--- a/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounter.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounter.kt
@@ -12,6 +12,7 @@ import android.util.AttributeSet
import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.RelativeLayout
+import androidx.core.view.updatePadding
import kotlinx.android.synthetic.main.mozac_ui_tabcounter_layout.view.*
import org.mozilla.fenix.R
import java.text.NumberFormat
@@ -178,7 +179,7 @@ class TabCounter @JvmOverloads constructor(
private fun formatForDisplay(count: Int): String {
return if (count > MAX_VISIBLE_TABS) {
- counter_text.setPadding(0, 0, 0, INFINITE_CHAR_PADDING_BOTTOM)
+ counter_text.updatePadding(bottom = INFINITE_CHAR_PADDING_BOTTOM)
SO_MANY_TABS_OPEN
} else NumberFormat.getInstance().format(count.toLong())
}
diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt
index 5a8d88b13..27b47c309 100644
--- a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt
+++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarMenu.kt
@@ -30,6 +30,7 @@ interface ToolbarMenu {
object ReaderModeAppearance : Item()
object Bookmarks : Item()
object History : Item()
+ object Downloads : Item()
}
val menuBuilder: BrowserMenuBuilder
diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt
index 0e079b512..40c64d5bd 100644
--- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt
+++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt
@@ -4,19 +4,15 @@
package org.mozilla.fenix.customtabs
-import android.content.Intent
import androidx.navigation.NavDestination
import androidx.navigation.NavDirections
import mozilla.components.browser.session.runWithSession
-import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.manifest.WebAppManifestParser
import mozilla.components.feature.intent.ext.getSessionId
import mozilla.components.feature.pwa.ext.getWebAppManifest
-import mozilla.components.feature.search.SearchAdapter
import mozilla.components.support.utils.SafeIntent
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
-import org.mozilla.fenix.IntentReceiverActivity
import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
@@ -28,12 +24,6 @@ import java.security.InvalidParameterException
*/
open class ExternalAppBrowserActivity : HomeActivity() {
- private val openInFenixIntent by lazy {
- Intent(this, IntentReceiverActivity::class.java).apply {
- action = Intent.ACTION_VIEW
- }
- }
-
final override fun getBreadcrumbMessage(destination: NavDestination): String {
val fragmentName = resources.getResourceEntryName(destination.id)
return "Changing to fragment $fragmentName, isCustomTab: true"
@@ -45,8 +35,8 @@ open class ExternalAppBrowserActivity : HomeActivity() {
final override fun getIntentSessionId(intent: SafeIntent) = intent.getSessionId()
- override fun setAppAllStartTelemetry(safeIntent: SafeIntent) {
- components.appAllSourceStartTelemetry.receivedIntentInExternalAppBrowserActivity(safeIntent)
+ override fun startupTelemetryOnCreateCalled(safeIntent: SafeIntent, hasSavedInstanceState: Boolean) {
+ components.appStartupTelemetry.onExternalAppBrowserOnCreate(safeIntent, hasSavedInstanceState)
}
override fun getNavDirections(
@@ -73,19 +63,6 @@ open class ExternalAppBrowserActivity : HomeActivity() {
}
}
- override fun getSearchAdapter(store: BrowserStore): SearchAdapter {
- val baseAdapter = super.getSearchAdapter(store)
- return object : SearchAdapter {
-
- override fun sendSearch(isPrivate: Boolean, text: String) {
- baseAdapter.sendSearch(isPrivate, text)
- startActivity(openInFenixIntent)
- }
-
- override fun isPrivateSession() = baseAdapter.isPrivateSession()
- }
- }
-
override fun onDestroy() {
super.onDestroy()
diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/WebAppSiteControlsBuilder.kt b/app/src/main/java/org/mozilla/fenix/customtabs/WebAppSiteControlsBuilder.kt
index 78d7955e6..f6e6facf2 100644
--- a/app/src/main/java/org/mozilla/fenix/customtabs/WebAppSiteControlsBuilder.kt
+++ b/app/src/main/java/org/mozilla/fenix/customtabs/WebAppSiteControlsBuilder.kt
@@ -4,9 +4,9 @@
package org.mozilla.fenix.customtabs
+import android.app.Notification
import android.content.Context
import android.content.Intent
-import androidx.core.app.NotificationCompat
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.concept.engine.manifest.WebAppManifest
@@ -23,12 +23,8 @@ class WebAppSiteControlsBuilder(
private val inner = SiteControlsBuilder.CopyAndRefresh(reloadUrlUseCase)
- override fun buildNotification(
- context: Context,
- builder: NotificationCompat.Builder,
- channelId: String
- ) {
- inner.buildNotification(context, builder, channelId)
+ override fun buildNotification(context: Context, builder: Notification.Builder) {
+ inner.buildNotification(context, builder)
val isPrivateSession = sessionManager.findSessionById(sessionId)?.private ?: false
diff --git a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsAdapter.kt b/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsAdapter.kt
index b0050ecf8..3581ea988 100644
--- a/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsAdapter.kt
+++ b/app/src/main/java/org/mozilla/fenix/exceptions/ExceptionsAdapter.kt
@@ -11,6 +11,7 @@ import androidx.annotation.StringRes
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
+import mozilla.components.ui.widgets.WidgetSiteItemView
import org.mozilla.fenix.exceptions.viewholders.ExceptionsDeleteButtonViewHolder
import org.mozilla.fenix.exceptions.viewholders.ExceptionsHeaderViewHolder
import org.mozilla.fenix.exceptions.viewholders.ExceptionsListItemViewHolder
@@ -67,7 +68,7 @@ abstract class ExceptionsAdapter(
ExceptionsHeaderViewHolder.LAYOUT_ID ->
ExceptionsHeaderViewHolder(view, headerDescriptionResource)
ExceptionsListItemViewHolder.LAYOUT_ID ->
- ExceptionsListItemViewHolder(view, interactor)
+ ExceptionsListItemViewHolder(view as WidgetSiteItemView, interactor)
else -> throw IllegalStateException()
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsListItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsListItemViewHolder.kt
index d3225d34f..d403703e1 100644
--- a/app/src/main/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsListItemViewHolder.kt
+++ b/app/src/main/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsListItemViewHolder.kt
@@ -4,39 +4,41 @@
package org.mozilla.fenix.exceptions.viewholders
-import android.view.View
-import kotlinx.android.synthetic.main.exception_item.*
+import androidx.recyclerview.widget.RecyclerView
import mozilla.components.browser.icons.BrowserIcons
+import mozilla.components.ui.widgets.WidgetSiteItemView
import org.mozilla.fenix.R
import org.mozilla.fenix.exceptions.ExceptionsInteractor
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.loadIntoView
-import org.mozilla.fenix.utils.view.ViewHolder
/**
* View holder for a single website that is exempted from Tracking Protection or Logins.
*/
class ExceptionsListItemViewHolder(
- view: View,
+ private val view: WidgetSiteItemView,
private val interactor: ExceptionsInteractor,
private val icons: BrowserIcons = view.context.components.core.icons
-) : ViewHolder(view) {
+) : RecyclerView.ViewHolder(view) {
private lateinit var item: T
init {
- delete_exception.setOnClickListener {
+ view.setSecondaryButton(
+ icon = R.drawable.ic_close,
+ contentDescription = R.string.history_delete_item
+ ) {
interactor.onDeleteOne(item)
}
}
fun bind(item: T, url: String) {
this.item = item
- webAddressView.text = url
- icons.loadIntoView(favicon_image, url)
+ view.setText(label = url, caption = null)
+ icons.loadIntoView(view.iconView, url)
}
companion object {
- const val LAYOUT_ID = R.layout.exception_item
+ const val LAYOUT_ID = R.layout.site_list_item
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/ext/Activity.kt b/app/src/main/java/org/mozilla/fenix/ext/Activity.kt
index 7dd2b3dce..e65ad9f35 100644
--- a/app/src/main/java/org/mozilla/fenix/ext/Activity.kt
+++ b/app/src/main/java/org/mozilla/fenix/ext/Activity.kt
@@ -7,6 +7,7 @@ package org.mozilla.fenix.ext
import android.app.Activity
import android.view.View
import android.view.WindowManager
+import mozilla.components.support.base.crash.Breadcrumb
/**
* Attempts to call immersive mode using the View to hide the status bar and navigation buttons.
@@ -22,3 +23,19 @@ fun Activity.enterToImmersiveMode() {
or View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
}
+
+fun Activity.breadcrumb(
+ message: String,
+ data: Map = emptyMap()
+) {
+ components.analytics.crashReporter.recordCrashBreadcrumb(
+ Breadcrumb(
+ category = this::class.java.simpleName,
+ message = message,
+ data = data + mapOf(
+ "instance" to this.hashCode().toString()
+ ),
+ level = Breadcrumb.Level.INFO
+ )
+ )
+}
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 1e3d3b782..d10637026 100644
--- a/app/src/main/java/org/mozilla/fenix/ext/Context.kt
+++ b/app/src/main/java/org/mozilla/fenix/ext/Context.kt
@@ -6,6 +6,9 @@ package org.mozilla.fenix.ext
import android.app.Activity
import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.provider.Settings
import android.view.ContextThemeWrapper
import android.view.View
import android.view.ViewGroup
@@ -89,3 +92,21 @@ fun Context.getStringWithArgSafe(@StringRes resId: Int, formatArg: String): Stri
*/
val Context.accessibilityManager: AccessibilityManager get() =
getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+
+/**
+ * Used to navigate to system notifications settings for app
+ */
+fun Context.navigateToNotificationsSettings() {
+ val intent = Intent()
+ intent.let {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ it.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
+ it.putExtra(Settings.EXTRA_APP_PACKAGE, this.packageName)
+ } else {
+ it.action = "android.settings.APP_NOTIFICATION_SETTINGS"
+ it.putExtra("app_package", this.packageName)
+ it.putExtra("app_uid", this.applicationInfo.uid)
+ }
+ }
+ startActivity(intent)
+}
diff --git a/app/src/main/java/org/mozilla/fenix/ext/DownloadItem.kt b/app/src/main/java/org/mozilla/fenix/ext/DownloadItem.kt
new file mode 100644
index 000000000..93fa26aea
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/ext/DownloadItem.kt
@@ -0,0 +1,48 @@
+/* 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 org.mozilla.fenix.R
+import org.mozilla.fenix.library.downloads.DownloadItem
+
+// While this looks complex, it's actually pretty simple.
+@SuppressWarnings("ComplexMethod")
+fun DownloadItem.getIcon(): Int {
+ fun getIconCornerCases(fileName: String?): Int {
+ return when {
+ fileName?.endsWith("apk") == true -> R.drawable.ic_file_type_apk
+ fileName?.endsWith("zip") == true -> R.drawable.ic_file_type_zip
+ else -> R.drawable.ic_file_type_default
+ }
+ }
+
+ fun checkForApplicationArchiveSubtypes(contentType: String): Int? {
+ return when {
+ contentType.contains("rar") -> R.drawable.ic_file_type_zip
+ contentType.contains("zip") -> R.drawable.ic_file_type_zip
+ contentType.contains("7z") -> R.drawable.ic_file_type_zip
+ contentType.contains("tar") -> R.drawable.ic_file_type_zip
+ contentType.contains("freearc") -> R.drawable.ic_file_type_zip
+ contentType.contains("octet-stream") -> null
+ contentType.contains("vnd.android.package-archive") -> null
+ else -> R.drawable.ic_file_type_document
+ }
+ }
+
+ fun getIconFromContentType(contentType: String): Int? {
+ return when {
+ contentType.contains("image/") -> R.drawable.ic_file_type_image
+ contentType.contains("audio/") -> R.drawable.ic_file_type_audio_note
+ contentType.contains("video/") -> R.drawable.ic_file_type_video
+ contentType.contains("application/") -> checkForApplicationArchiveSubtypes(contentType)
+ contentType.contains("text/") -> R.drawable.ic_file_type_document
+ else -> null
+ }
+ }
+
+ return contentType?.let { contentType ->
+ getIconFromContentType(contentType)
+ } ?: getIconCornerCases(fileName)
+}
diff --git a/app/src/main/java/org/mozilla/fenix/ext/EditText.kt b/app/src/main/java/org/mozilla/fenix/ext/EditText.kt
new file mode 100644
index 000000000..ac7934b86
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/ext/EditText.kt
@@ -0,0 +1,14 @@
+/* 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.widget.EditText
+
+/**
+ * Places cursor at the end of an EditText.
+ */
+fun EditText.placeCursorAtEnd() {
+ this.text?.length?.let { setSelection(it, it) }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt b/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt
index f4128e335..39c3f7bb4 100644
--- a/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/ext/Fragment.kt
@@ -12,6 +12,7 @@ import androidx.navigation.NavDirections
import androidx.navigation.NavOptions
import androidx.navigation.fragment.NavHostFragment.findNavController
import androidx.navigation.fragment.findNavController
+import mozilla.components.support.base.crash.Breadcrumb
import org.mozilla.fenix.NavHostActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.Components
@@ -57,3 +58,23 @@ fun Fragment.redirectToReAuth(destinations: List, currentDestination: Int?)
findNavController().popBackStack(R.id.savedLoginsAuthFragment, false)
}
}
+
+fun Fragment.breadcrumb(
+ message: String,
+ data: Map = emptyMap()
+) {
+ val activityName = activity?.let { it::class.java.simpleName } ?: "null"
+
+ requireComponents.analytics.crashReporter.recordCrashBreadcrumb(
+ Breadcrumb(
+ category = this::class.java.simpleName,
+ message = message,
+ data = data + mapOf(
+ "instance" to hashCode().toString(),
+ "activityInstance" to activity?.hashCode().toString(),
+ "activityName" to activityName
+ ),
+ level = Breadcrumb.Level.INFO
+ )
+ )
+}
diff --git a/app/src/main/java/org/mozilla/fenix/ext/List.kt b/app/src/main/java/org/mozilla/fenix/ext/List.kt
new file mode 100644
index 000000000..95510f66f
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/ext/List.kt
@@ -0,0 +1,20 @@
+/* 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 org.mozilla.fenix.library.downloads.DownloadItem
+import java.io.File
+
+/**
+ * Checks a List of DownloadItems to verify whether items
+ * on that list are present on the disk or not. If a user has
+ * deleted the downloaded item it should not show on the downloaded
+ * list.
+ */
+fun List.filterNotExistsOnDisk(): List {
+ return this.filter {
+ File(it.filePath).exists()
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/ext/LiveData.kt b/app/src/main/java/org/mozilla/fenix/ext/LiveData.kt
deleted file mode 100644
index 718385219..000000000
--- a/app/src/main/java/org/mozilla/fenix/ext/LiveData.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.fenix.ext
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.Observer
-
-/**
- * Observe a LiveData once and unregister from it as soon as the live data returns a value
- */
-fun LiveData.observeOnce(observer: (T) -> Unit) {
- observeForever(object : Observer {
- override fun onChanged(value: T) {
- removeObserver(this)
- observer(value)
- }
- })
-}
diff --git a/app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt b/app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt
index 68a2b2e61..8a8de7dce 100644
--- a/app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt
+++ b/app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt
@@ -9,7 +9,7 @@ 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.content.getColorFromAttr
import mozilla.components.support.ktx.android.util.dpToPx
fun SpannableString.setTextSize(context: Context, textSize: Int) =
@@ -23,10 +23,7 @@ fun SpannableString.setTextSize(context: Context, textSize: Int) =
fun SpannableString.setTextColor(context: Context, colorResId: Int) =
this.setSpan(
ForegroundColorSpan(
- ContextCompat.getColor(
- context,
- colorResId
- )
+ context.getColorFromAttr(colorResId)
),
0,
this.length,
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 1becb7b8d..bcacb3ea6 100644
--- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
@@ -16,7 +16,6 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.view.accessibility.AccessibilityEvent
import android.widget.Button
import android.widget.LinearLayout
import android.widget.PopupWindow
@@ -68,10 +67,13 @@ import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.feature.tab.collections.TabCollection
-import mozilla.components.feature.top.sites.TopSite
+import mozilla.components.feature.top.sites.TopSitesConfig
+import mozilla.components.feature.top.sites.TopSitesFeature
import mozilla.components.lib.state.ext.consumeFrom
+import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import mozilla.components.support.ktx.android.util.dpToPx
import org.mozilla.fenix.BrowserDirection
+import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BrowserAnimator.Companion.getToolbarNavOptions
@@ -97,6 +99,7 @@ import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
import org.mozilla.fenix.home.sessioncontrol.SessionControlView
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
+import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.DefaultTopSitesView
import org.mozilla.fenix.onboarding.FenixOnboarding
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.settings.SupportUtils.SumoTopic.HELP
@@ -146,8 +149,11 @@ class HomeFragment : Fragment() {
private val store: BrowserStore
get() = requireComponents.core.store
- private val onboarding by lazy { StrictMode.allowThreadDiskReads().resetPoliciesAfter {
- FenixOnboarding(requireContext()) } }
+ private val onboarding by lazy {
+ StrictMode.allowThreadDiskReads().resetPoliciesAfter {
+ FenixOnboarding(requireContext())
+ }
+ }
private lateinit var homeFragmentStore: HomeFragmentStore
private var _sessionControlInteractor: SessionControlInteractor? = null
@@ -157,6 +163,8 @@ class HomeFragment : Fragment() {
private var sessionControlView: SessionControlView? = null
private lateinit var currentMode: CurrentMode
+ private val topSitesFeature = ViewBoundFeatureWrapper()
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
@@ -190,22 +198,31 @@ class HomeFragment : Fragment() {
collections = components.core.tabCollectionStorage.cachedTabCollections,
expandedCollections = emptySet(),
mode = currentMode.getCurrentMode(),
- topSites = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
- components.core.topSiteStorage.cachedTopSites
- },
- tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip()
+ topSites = components.core.topSiteStorage.cachedTopSites,
+ tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip(),
+ showCollectionPlaceholder = components.settings.showCollectionsPlaceholderOnHome
)
)
}
+ topSitesFeature.set(
+ feature = TopSitesFeature(
+ view = DefaultTopSitesView(homeFragmentStore),
+ storage = components.core.topSiteStorage,
+ config = ::getTopSitesConfig
+ ),
+ owner = this,
+ view = view
+ )
+
_sessionControlInteractor = SessionControlInteractor(
DefaultSessionControlController(
activity = activity,
+ settings = components.settings,
engine = components.core.engine,
metrics = components.analytics.metrics,
sessionManager = sessionManager,
tabCollectionStorage = components.core.tabCollectionStorage,
- topSiteStorage = components.core.topSiteStorage,
addTabUseCase = components.useCases.tabsUseCases.addTab,
fragmentStore = homeFragmentStore,
navController = findNavController(),
@@ -220,9 +237,9 @@ class HomeFragment : Fragment() {
updateLayout(view)
sessionControlView = SessionControlView(
view.sessionControlRecyclerView,
+ viewLifecycleOwner,
sessionControlInteractor,
- homeViewModel,
- requireComponents.core.store.state.normalTabs.isNotEmpty()
+ homeViewModel
)
updateSessionControlView(view)
@@ -231,6 +248,15 @@ class HomeFragment : Fragment() {
return view
}
+ /**
+ * Returns a [TopSitesConfig] which specifies how many top sites to display and whether or
+ * not frequently visited sites should be displayed.
+ */
+ private fun getTopSitesConfig(): TopSitesConfig {
+ val settings = requireContext().settings()
+ return TopSitesConfig(settings.topSitesMaxLimit, settings.showTopFrecentSites)
+ }
+
/**
* The [SessionControlView] is forced to update with our current state when we call
* [HomeFragment.onCreateView] in order to be able to draw everything at once with the current
@@ -344,7 +370,7 @@ class HomeFragment : Fragment() {
view.toolbar_wrapper.setOnLongClickListener {
ToolbarPopupWindow.show(
- WeakReference(view),
+ WeakReference(it),
handlePasteAndGo = sessionControlInteractor::onPasteAndGo,
handlePaste = sessionControlInteractor::onPaste,
copyVisible = false
@@ -374,7 +400,8 @@ class HomeFragment : Fragment() {
// We call this onLayout so that the bottom bar width is correctly set for us to center
// the CFR in.
view.toolbar_wrapper.doOnLayout {
- if (!browsingModeManager.mode.isPrivate) {
+ val willNavigateToSearch = !bundleArgs.getBoolean(FOCUS_ON_ADDRESS_BAR) && FeatureFlags.newSearchExperience
+ if (!browsingModeManager.mode.isPrivate && !willNavigateToSearch) {
SearchWidgetCFR(
context = view.context,
settings = view.context.settings(),
@@ -385,19 +412,6 @@ class HomeFragment : Fragment() {
}
}
- val args by navArgs()
-
- if (view.context.settings().accessibilityServicesEnabled &&
- args.focusOnAddressBar
- ) {
- // We cannot put this in the fragment_home.xml file as it breaks tests
- view.toolbar_wrapper.isFocusableInTouchMode = true
- viewLifecycleOwner.lifecycleScope.launch {
- view.toolbar_wrapper?.requestFocus()
- view.toolbar_wrapper?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
- }
- }
-
if (browsingModeManager.mode.isPrivate) {
requireActivity().window.addFlags(FLAG_SECURE)
} else {
@@ -418,7 +432,7 @@ class HomeFragment : Fragment() {
updateTabCounter(requireComponents.core.store.state)
- if (args.focusOnAddressBar && requireContext().settings().useNewSearchExperience) {
+ if (bundleArgs.getBoolean(FOCUS_ON_ADDRESS_BAR) && FeatureFlags.newSearchExperience) {
navigateToSearch()
}
}
@@ -506,7 +520,6 @@ class HomeFragment : Fragment() {
override fun onStart() {
super.onStart()
subscribeToTabCollections()
- subscribeToTopSites()
val context = requireContext()
val components = context.components
@@ -516,7 +529,8 @@ class HomeFragment : Fragment() {
collections = components.core.tabCollectionStorage.cachedTabCollections,
mode = currentMode.getCurrentMode(),
topSites = components.core.topSiteStorage.cachedTopSites,
- tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip()
+ tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip(),
+ showCollectionPlaceholder = components.settings.showCollectionsPlaceholderOnHome
)
)
@@ -556,6 +570,10 @@ class HomeFragment : Fragment() {
// We only want this observer live just before we navigate away to the collection creation screen
requireComponents.core.tabCollectionStorage.unregister(collectionStorageObserver)
+
+ lifecycleScope.launch(IO) {
+ requireComponents.reviewPromptController.promptReview(requireActivity())
+ }
}
private fun dispatchModeChanges(mode: Mode) {
@@ -679,7 +697,7 @@ class HomeFragment : Fragment() {
}
private fun navigateToSearch() {
- val directions = if (requireContext().settings().useNewSearchExperience) {
+ val directions = if (FeatureFlags.newSearchExperience) {
HomeFragmentDirections.actionGlobalSearchDialog(
sessionId = null
)
@@ -771,6 +789,15 @@ class HomeFragment : Fragment() {
HomeFragmentDirections.actionGlobalHistoryFragment()
)
}
+
+ HomeMenu.Item.Downloads -> {
+ hideOnboardingIfNeeded()
+ nav(
+ R.id.homeFragment,
+ HomeFragmentDirections.actionGlobalDownloadsFragment()
+ )
+ }
+
HomeMenu.Item.Help -> {
hideOnboardingIfNeeded()
(activity as HomeActivity).openToBrowserAndLoad(
@@ -832,17 +859,6 @@ class HomeFragment : Fragment() {
}
}
- private fun subscribeToTopSites(): Observer> {
- return Observer> { topSites ->
- requireComponents.core.topSiteStorage.cachedTopSites = topSites
- context?.settings()?.preferences?.edit()
- ?.putInt(getString(R.string.pref_key_top_sites_size), topSites.size)?.apply()
- homeFragmentStore.dispatch(HomeFragmentAction.TopSitesChange(topSites))
- }.also { observer ->
- requireComponents.core.topSiteStorage.getTopSites().observe(this, observer)
- }
- }
-
private fun registerCollectionStorageObserver() {
requireComponents.core.tabCollectionStorage.register(collectionStorageObserver, this)
}
diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt
index bbe46024a..dc6b78d7d 100644
--- a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt
@@ -41,13 +41,16 @@ data class Tab(
* @property mode The state of the [HomeFragment] UI.
* @property tabs The list of opened [Tab] in the [HomeFragment].
* @property topSites The list of [TopSite] in the [HomeFragment].
+ * @property tip The current [Tip] to show on the [HomeFragment].
+ * @property showCollectionPlaceholder If true, shows a placeholder when there are no collections.
*/
data class HomeFragmentState(
val collections: List,
val expandedCollections: Set,
val mode: Mode,
val topSites: List,
- val tip: Tip? = null
+ val tip: Tip? = null,
+ val showCollectionPlaceholder: Boolean
) : State
sealed class HomeFragmentAction : Action {
@@ -55,7 +58,8 @@ sealed class HomeFragmentAction : Action {
val topSites: List,
val mode: Mode,
val collections: List,
- val tip: Tip? = null
+ val tip: Tip? = null,
+ val showCollectionPlaceholder: Boolean
) :
HomeFragmentAction()
@@ -66,6 +70,7 @@ sealed class HomeFragmentAction : Action {
data class ModeChange(val mode: Mode) : HomeFragmentAction()
data class TopSitesChange(val topSites: List) : HomeFragmentAction()
data class RemoveTip(val tip: Tip) : HomeFragmentAction()
+ object RemoveCollectionsPlaceholder : HomeFragmentAction()
}
private fun homeFragmentStateReducer(
@@ -93,6 +98,11 @@ private fun homeFragmentStateReducer(
is HomeFragmentAction.CollectionsChange -> state.copy(collections = action.collections)
is HomeFragmentAction.ModeChange -> state.copy(mode = action.mode)
is HomeFragmentAction.TopSitesChange -> state.copy(topSites = action.topSites)
- is HomeFragmentAction.RemoveTip -> { state.copy(tip = null) }
+ is HomeFragmentAction.RemoveTip -> {
+ state.copy(tip = null)
+ }
+ is HomeFragmentAction.RemoveCollectionsPlaceholder -> {
+ state.copy(showCollectionPlaceholder = false)
+ }
}
}
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 1ab68040b..579e4bf95 100644
--- a/app/src/main/java/org/mozilla/fenix/home/HomeMenu.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/HomeMenu.kt
@@ -43,6 +43,7 @@ class HomeMenu(
object SyncedTabs : Item()
object History : Item()
object Bookmarks : Item()
+ object Downloads : Item()
object Quit : Item()
object Sync : Item()
}
@@ -144,6 +145,14 @@ class HomeMenu(
onItemTapped.invoke(Item.Help)
}
+ val downloadsItem = BrowserMenuImageText(
+ "Downloads",
+ R.drawable.ic_download,
+ primaryTextColor
+ ) {
+ onItemTapped.invoke(Item.Downloads)
+ }
+
// Only query account manager if it has been initialized.
// We don't want to cause its initialization just for this check.
val accountAuthItem = if (context.components.backgroundServices.accountManagerAvailableQueue.isReady()) {
@@ -158,9 +167,10 @@ class HomeMenu(
if (settings.shouldDeleteBrowsingDataOnQuit) quitItem else null,
settingsItem,
BrowserMenuDivider(),
- if (FeatureFlags.syncedTabs) syncedTabsItem else null,
+ if (settings.syncedTabsInTabsTray) null else syncedTabsItem,
bookmarksItem,
historyItem,
+ if (FeatureFlags.viewDownloads) downloadsItem else null,
BrowserMenuDivider(),
addons,
BrowserMenuDivider(),
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt
index 6acca7d2e..bde850a17 100644
--- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt
@@ -8,11 +8,13 @@ import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.LayoutRes
+import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.top.sites.TopSite
+import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.tips.Tip
import org.mozilla.fenix.home.OnboardingState
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionHeaderViewHolder
@@ -20,7 +22,7 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoCollectionsMessageViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.PrivateBrowsingDescriptionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder
-import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSiteViewHolder
+import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSitePagerViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingAutomaticSignInViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingFinishViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingHeaderViewHolder
@@ -40,14 +42,14 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
ButtonTipViewHolder.LAYOUT_ID
)
- data class TopSiteList(val topSites: List) : AdapterItem(TopSiteViewHolder.LAYOUT_ID) {
+ data class TopSitePager(val topSites: List) : AdapterItem(TopSitePagerViewHolder.LAYOUT_ID) {
override fun sameAs(other: AdapterItem): Boolean {
- val newTopSites = (other as? TopSiteList) ?: return false
+ val newTopSites = (other as? TopSitePager) ?: return false
return newTopSites.topSites == this.topSites
}
override fun contentsSameAs(other: AdapterItem): Boolean {
- val newTopSites = (other as? TopSiteList) ?: return false
+ val newTopSites = (other as? TopSitePager) ?: return false
if (newTopSites.topSites.size != this.topSites.size) return false
val newSitesSequence = newTopSites.topSites.asSequence()
val oldTopSites = this.topSites.asSequence()
@@ -135,7 +137,8 @@ class AdapterItemDiffCallback : DiffUtil.ItemCallback() {
class SessionControlAdapter(
private val interactor: SessionControlInteractor,
- private val hasNormalTabsOpened: Boolean
+ private val viewLifecycleOwner: LifecycleOwner,
+ private val components: Components
) : ListAdapter(AdapterItemDiffCallback()) {
// This method triggers the ComplexMethod lint error when in fact it's quite simple.
@@ -144,13 +147,18 @@ class SessionControlAdapter(
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return when (viewType) {
ButtonTipViewHolder.LAYOUT_ID -> ButtonTipViewHolder(view, interactor)
- TopSiteViewHolder.LAYOUT_ID -> TopSiteViewHolder(view, interactor)
+ TopSitePagerViewHolder.LAYOUT_ID -> TopSitePagerViewHolder(view, interactor)
PrivateBrowsingDescriptionViewHolder.LAYOUT_ID -> PrivateBrowsingDescriptionViewHolder(
view,
interactor
)
NoCollectionsMessageViewHolder.LAYOUT_ID ->
- NoCollectionsMessageViewHolder(view, interactor, hasNormalTabsOpened)
+ NoCollectionsMessageViewHolder(
+ view,
+ viewLifecycleOwner,
+ components.core.store,
+ interactor
+ )
CollectionHeaderViewHolder.LAYOUT_ID -> CollectionHeaderViewHolder(view)
CollectionViewHolder.LAYOUT_ID -> CollectionViewHolder(view, interactor)
TabInCollectionViewHolder.LAYOUT_ID -> TabInCollectionViewHolder(
@@ -195,8 +203,8 @@ class SessionControlAdapter(
val tipItem = item as AdapterItem.TipItem
holder.bind(tipItem.tip)
}
- is TopSiteViewHolder -> {
- holder.bind((item as AdapterItem.TopSiteList).topSites)
+ is TopSitePagerViewHolder -> {
+ holder.bind((item as AdapterItem.TopSitePager).topSites)
}
is CollectionViewHolder -> {
val (collection, expanded) = item as AdapterItem.CollectionItem
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 a5b4b7de7..7c927d2c5 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
@@ -22,7 +22,6 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.collections.SaveCollectionStep
import org.mozilla.fenix.components.TabCollectionStorage
-import org.mozilla.fenix.components.TopSiteStorage
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.components.metrics.MetricsUtils
@@ -37,6 +36,7 @@ import org.mozilla.fenix.home.HomeFragmentAction
import org.mozilla.fenix.home.HomeFragmentDirections
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.settings.SupportUtils
+import org.mozilla.fenix.utils.Settings
import mozilla.components.feature.tab.collections.Tab as ComponentTab
/**
@@ -144,16 +144,21 @@ interface SessionControlController {
* @see [CollectionInteractor.onAddTabsToCollectionTapped]
*/
fun handleCreateCollection()
+
+ /**
+ * @see [CollectionInteractor.onRemoveCollectionsPlaceholder]
+ */
+ fun handleRemoveCollectionsPlaceholder()
}
@Suppress("TooManyFunctions", "LargeClass")
class DefaultSessionControlController(
private val activity: HomeActivity,
+ private val settings: Settings,
private val engine: Engine,
private val metrics: MetricController,
private val sessionManager: SessionManager,
private val tabCollectionStorage: TabCollectionStorage,
- private val topSiteStorage: TopSiteStorage,
private val addTabUseCase: TabsUseCases.AddNewTabUseCase,
private val fragmentStore: HomeFragmentStore,
private val navController: NavController,
@@ -213,7 +218,11 @@ class DefaultSessionControlController(
metrics.track(Event.CollectionAllTabsRestored)
}
- override fun handleCollectionRemoveTab(collection: TabCollection, tab: ComponentTab, wasSwiped: Boolean) {
+ override fun handleCollectionRemoveTab(
+ collection: TabCollection,
+ tab: ComponentTab,
+ wasSwiped: Boolean
+ ) {
metrics.track(Event.CollectionTabRemoved)
if (collection.tabs.size == 1) {
@@ -223,7 +232,13 @@ class DefaultSessionControlController(
)
val message =
activity.resources.getString(R.string.delete_tab_and_collection_dialog_message)
- showDeleteCollectionPrompt(collection, title, message, wasSwiped, handleSwipedItemDeletionCancel)
+ showDeleteCollectionPrompt(
+ collection,
+ title,
+ message,
+ wasSwiped,
+ handleSwipedItemDeletionCancel
+ )
} else {
viewLifecycleScope.launch(Dispatchers.IO) {
tabCollectionStorage.removeTabFromCollection(collection, tab)
@@ -273,7 +288,9 @@ class DefaultSessionControlController(
}
viewLifecycleScope.launch(Dispatchers.IO) {
- topSiteStorage.removeTopSite(topSite)
+ with(activity.components.useCases.topSitesUseCase) {
+ removeTopSites(topSite)
+ }
}
}
@@ -369,6 +386,11 @@ class DefaultSessionControlController(
showTabTrayCollectionCreation()
}
+ override fun handleRemoveCollectionsPlaceholder() {
+ settings.showCollectionsPlaceholderOnHome = false
+ fragmentStore.dispatch(HomeFragmentAction.RemoveCollectionsPlaceholder)
+ }
+
private fun showShareFragment(shareSubject: String, data: List) {
val directions = HomeFragmentDirections.actionGlobalShareFragment(
shareSubject = shareSubject,
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt
index 644178f38..b33b97699 100644
--- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt
@@ -93,6 +93,11 @@ interface CollectionInteractor {
* Opens the collection creator
*/
fun onAddTabsToCollectionTapped()
+
+ /**
+ * User has removed the collections placeholder from home.
+ */
+ fun onRemoveCollectionsPlaceholder()
}
interface ToolbarInteractor {
@@ -256,4 +261,8 @@ class SessionControlInteractor(
override fun onPaste(clipboardText: String) {
controller.handlePaste(clipboardText)
}
+
+ override fun onRemoveCollectionsPlaceholder() {
+ controller.handleRemoveCollectionsPlaceholder()
+ }
}
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt
index c2f363140..5aaaa3854 100644
--- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt
@@ -5,6 +5,7 @@
package org.mozilla.fenix.home.sessioncontrol
import android.view.View
+import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -13,6 +14,7 @@ import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.top.sites.TopSite
import org.mozilla.fenix.R
import org.mozilla.fenix.components.tips.Tip
+import org.mozilla.fenix.ext.components
import org.mozilla.fenix.home.HomeFragmentState
import org.mozilla.fenix.home.HomeScreenViewModel
import org.mozilla.fenix.home.Mode
@@ -25,19 +27,21 @@ private fun normalModeAdapterItems(
topSites: List,
collections: List,
expandedCollections: Set,
- tip: Tip?
+ tip: Tip?,
+ showCollectionsPlaceholder: Boolean
): List {
val items = mutableListOf()
tip?.let { items.add(AdapterItem.TipItem(it)) }
if (topSites.isNotEmpty()) {
- items.add(AdapterItem.TopSiteList(topSites))
+ items.add(AdapterItem.TopSitePager(topSites))
}
if (collections.isEmpty()) {
- items.add(AdapterItem.CollectionHeader)
- items.add(AdapterItem.NoCollectionsMessage)
+ if (showCollectionsPlaceholder) {
+ items.add(AdapterItem.NoCollectionsMessage)
+ }
} else {
showCollections(collections, expandedCollections, items)
}
@@ -68,62 +72,77 @@ private fun onboardingAdapterItems(onboardingState: OnboardingState): List = mutableListOf(AdapterItem.OnboardingHeader)
// Customize FxA items based on where we are with the account state:
- items.addAll(when (onboardingState) {
- OnboardingState.SignedOutNoAutoSignIn -> {
- listOf(
- AdapterItem.OnboardingManualSignIn
- )
- }
- is OnboardingState.SignedOutCanAutoSignIn -> {
- listOf(
- AdapterItem.OnboardingAutomaticSignIn(onboardingState)
- )
+ items.addAll(
+ when (onboardingState) {
+ OnboardingState.SignedOutNoAutoSignIn -> {
+ listOf(
+ AdapterItem.OnboardingManualSignIn
+ )
+ }
+ is OnboardingState.SignedOutCanAutoSignIn -> {
+ listOf(
+ AdapterItem.OnboardingAutomaticSignIn(onboardingState)
+ )
+ }
+ OnboardingState.SignedIn -> listOf()
}
- OnboardingState.SignedIn -> listOf()
- })
-
- items.addAll(listOf(
- AdapterItem.OnboardingSectionHeader {
- val appName = it.getString(R.string.app_name)
- it.getString(R.string.onboarding_feature_section_header, appName)
- },
- AdapterItem.OnboardingWhatsNew,
- AdapterItem.OnboardingTrackingProtection,
- AdapterItem.OnboardingThemePicker,
- AdapterItem.OnboardingPrivateBrowsing,
- AdapterItem.OnboardingToolbarPositionPicker,
- AdapterItem.OnboardingPrivacyNotice,
- AdapterItem.OnboardingFinish
- ))
+ )
+
+ items.addAll(
+ listOf(
+ AdapterItem.OnboardingSectionHeader {
+ val appName = it.getString(R.string.app_name)
+ it.getString(R.string.onboarding_feature_section_header, appName)
+ },
+ AdapterItem.OnboardingWhatsNew,
+ AdapterItem.OnboardingTrackingProtection,
+ AdapterItem.OnboardingThemePicker,
+ AdapterItem.OnboardingPrivateBrowsing,
+ AdapterItem.OnboardingToolbarPositionPicker,
+ AdapterItem.OnboardingPrivacyNotice,
+ AdapterItem.OnboardingFinish
+ )
+ )
return items
}
private fun HomeFragmentState.toAdapterList(): List = when (mode) {
- is Mode.Normal -> normalModeAdapterItems(topSites, collections, expandedCollections, tip)
+ is Mode.Normal -> normalModeAdapterItems(
+ topSites,
+ collections,
+ expandedCollections,
+ tip,
+ showCollectionPlaceholder
+ )
is Mode.Private -> privateModeAdapterItems()
is Mode.Onboarding -> onboardingAdapterItems(mode.state)
}
-private fun collectionTabItems(collection: TabCollection) = collection.tabs.mapIndexed { index, tab ->
+private fun collectionTabItems(collection: TabCollection) =
+ collection.tabs.mapIndexed { index, tab ->
AdapterItem.TabInCollectionItem(collection, tab, index == collection.tabs.lastIndex)
-}
+ }
class SessionControlView(
- override val containerView: View?,
+ override val containerView: View,
+ viewLifecycleOwner: LifecycleOwner,
interactor: SessionControlInteractor,
- private var homeScreenViewModel: HomeScreenViewModel,
- private val hasNormalTabsOpened: Boolean
+ private var homeScreenViewModel: HomeScreenViewModel
) : LayoutContainer {
val view: RecyclerView = containerView as RecyclerView
- private val sessionControlAdapter = SessionControlAdapter(interactor, hasNormalTabsOpened)
+ private val sessionControlAdapter = SessionControlAdapter(
+ interactor,
+ viewLifecycleOwner,
+ containerView.context.components
+ )
init {
view.apply {
adapter = sessionControlAdapter
- layoutManager = LinearLayoutManager(containerView!!.context)
+ layoutManager = LinearLayoutManager(containerView.context)
val itemTouchHelper =
ItemTouchHelper(
SwipeToDeleteCallback(
@@ -141,7 +160,7 @@ class SessionControlView(
sessionControlAdapter.submitList(stateAdapterList) {
val loadedTopSites = stateAdapterList.find { adapterItem ->
- adapterItem is AdapterItem.TopSiteList && adapterItem.topSites.isNotEmpty()
+ adapterItem is AdapterItem.TopSitePager && adapterItem.topSites.isNotEmpty()
}
loadedTopSites?.run {
homeScreenViewModel.shouldScrollToTopSites = false
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolder.kt
index 00a42acdc..ed3c128d0 100644
--- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolder.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolder.kt
@@ -6,22 +6,50 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.View
import androidx.core.view.isVisible
+import androidx.lifecycle.LifecycleOwner
import kotlinx.android.synthetic.main.no_collections_message.*
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.map
+import mozilla.components.browser.state.selector.normalTabs
+import mozilla.components.browser.state.store.BrowserStore
+import mozilla.components.lib.state.ext.flowScoped
+import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.R
-import org.mozilla.fenix.utils.view.ViewHolder
+import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
+import org.mozilla.fenix.utils.view.ViewHolder
+@OptIn(ExperimentalCoroutinesApi::class)
open class NoCollectionsMessageViewHolder(
view: View,
- interactor: CollectionInteractor,
- hasNormalTabsOpened: Boolean
+ viewLifecycleOwner: LifecycleOwner,
+ store: BrowserStore,
+ interactor: CollectionInteractor
) : ViewHolder(view) {
init {
add_tabs_to_collections_button.setOnClickListener {
interactor.onAddTabsToCollectionTapped()
}
- add_tabs_to_collections_button.isVisible = hasNormalTabsOpened
+
+ remove_collection_placeholder.increaseTapArea(
+ view.resources.getDimensionPixelSize(R.dimen.tap_increase_16)
+ )
+
+ remove_collection_placeholder.setOnClickListener {
+ interactor.onRemoveCollectionsPlaceholder()
+ }
+
+ add_tabs_to_collections_button.isVisible = store.state.normalTabs.isNotEmpty()
+
+ store.flowScoped(viewLifecycleOwner) { flow ->
+ flow.map { state -> state.normalTabs.size }
+ .ifChanged()
+ .collect { tabs ->
+ add_tabs_to_collections_button.isVisible = tabs > 0
+ }
+ }
}
companion object {
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSitePagerViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSitePagerViewHolder.kt
new file mode 100644
index 000000000..ab0142fa1
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSitePagerViewHolder.kt
@@ -0,0 +1,57 @@
+/* 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.home.sessioncontrol.viewholders
+
+import android.view.View
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewpager2.widget.ViewPager2
+import kotlinx.android.synthetic.main.component_top_sites_pager.view.*
+import mozilla.components.feature.top.sites.TopSite
+import org.mozilla.fenix.R
+import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
+import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSitesPagerAdapter
+
+class TopSitePagerViewHolder(
+ view: View,
+ interactor: TopSiteInteractor
+) : RecyclerView.ViewHolder(view) {
+
+ private val topSitesPagerAdapter = TopSitesPagerAdapter(interactor)
+ private val pageIndicator = view.page_indicator
+
+ private val topSitesPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {
+ override fun onPageSelected(position: Int) {
+ pageIndicator.setSelection(position)
+ }
+ }
+
+ init {
+ view.top_sites_pager.apply {
+ adapter = topSitesPagerAdapter
+ registerOnPageChangeCallback(topSitesPageChangeCallback)
+ }
+ }
+
+ fun bind(topSites: List) {
+ topSitesPagerAdapter.updateData(topSites)
+
+ // Don't show any page indicator if there is only 1 page.
+ val numPages = if (topSites.size > TOP_SITES_PER_PAGE) {
+ TOP_SITES_MAX_PAGE_SIZE
+ } else {
+ 0
+ }
+
+ pageIndicator.isVisible = numPages > 1
+ pageIndicator.setSize(numPages)
+ }
+
+ companion object {
+ const val LAYOUT_ID = R.layout.component_top_sites_pager
+ const val TOP_SITES_MAX_PAGE_SIZE = 2
+ const val TOP_SITES_PER_PAGE = 8
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSiteViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSiteViewHolder.kt
index 44359036e..7f1d9b91e 100644
--- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSiteViewHolder.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSiteViewHolder.kt
@@ -6,7 +6,6 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.View
import androidx.recyclerview.widget.RecyclerView
-import com.google.android.flexbox.FlexboxLayoutManager
import kotlinx.android.synthetic.main.component_top_sites.view.*
import mozilla.components.feature.top.sites.TopSite
import org.mozilla.fenix.R
@@ -23,8 +22,6 @@ class TopSiteViewHolder(
init {
view.top_sites_list.apply {
adapter = topSitesAdapter
- layoutManager = FlexboxLayoutManager(view.context)
- isNestedScrollingEnabled = false
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingAutomaticSignInViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingAutomaticSignInViewHolder.kt
index 39c49a414..c09fb31d2 100644
--- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingAutomaticSignInViewHolder.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingAutomaticSignInViewHolder.kt
@@ -31,9 +31,9 @@ class OnboardingAutomaticSignInViewHolder(
private val headerText = view.header_text
init {
- view.turn_on_sync_button.setOnClickListener {
+ view.fxa_sign_in_button.setOnClickListener {
scope.launch {
- onClick(it.turn_on_sync_button)
+ onClick(it.fxa_sign_in_button)
}
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolder.kt
index e4fa6d5a3..98155f061 100644
--- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolder.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolder.kt
@@ -5,40 +5,40 @@
package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
import android.view.View
-import androidx.core.content.ContextCompat
import androidx.navigation.Navigation
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.onboarding_manual_signin.view.*
-import mozilla.components.support.ktx.android.content.getDrawableWithTint
-import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
+import org.mozilla.fenix.ext.addUnderline
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.home.HomeFragmentDirections
+import org.mozilla.fenix.onboarding.OnboardingController
+import org.mozilla.fenix.onboarding.OnboardingInteractor
class OnboardingManualSignInViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val headerText = view.header_text
init {
- view.turn_on_sync_button.setOnClickListener {
+ val interactor = OnboardingInteractor(OnboardingController(itemView.context))
+
+ view.fxa_sign_in_button.setOnClickListener {
it.context.components.analytics.metrics.track(Event.OnboardingManualSignIn)
val directions = HomeFragmentDirections.actionGlobalTurnOnSync()
Navigation.findNavController(view).navigate(directions)
}
+
+ view.learn_more.addUnderline()
+ view.learn_more.setOnClickListener {
+ interactor.onLearnMoreClicked()
+ }
}
fun bind() {
val context = itemView.context
-
- val appName = context.getString(R.string.app_name)
- headerText.text = context.getString(R.string.onboarding_firefox_account_header, appName)
- val icon = context.getDrawableWithTint(
- R.drawable.ic_onboarding_firefox_accounts,
- ContextCompat.getColor(context, R.color.white_color)
- )
- headerText.putCompoundDrawablesRelativeWithIntrinsicBounds(start = icon)
+ headerText.text = context.getString(R.string.onboarding_account_sign_in_header)
}
companion object {
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/DefaultTopSitesView.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/DefaultTopSitesView.kt
new file mode 100644
index 000000000..63fd9c2c9
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/DefaultTopSitesView.kt
@@ -0,0 +1,19 @@
+/* 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.home.sessioncontrol.viewholders.topsites
+
+import mozilla.components.feature.top.sites.TopSite
+import mozilla.components.feature.top.sites.view.TopSitesView
+import org.mozilla.fenix.home.HomeFragmentAction
+import org.mozilla.fenix.home.HomeFragmentStore
+
+class DefaultTopSitesView(
+ val store: HomeFragmentStore
+) : TopSitesView {
+
+ override fun displayTopSites(topSites: List) {
+ store.dispatch(HomeFragmentAction.TopSitesChange(topSites))
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/PagerIndicator.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/PagerIndicator.kt
new file mode 100644
index 000000000..dd5acdf5e
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/PagerIndicator.kt
@@ -0,0 +1,82 @@
+/* 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.home.sessioncontrol.viewholders.topsites
+
+import android.content.Context
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.View
+import android.widget.LinearLayout
+import androidx.core.view.MarginLayoutParamsCompat
+import org.mozilla.fenix.R
+
+/**
+ * A pager indicator widget to display the number of pages and the current selected page.
+ */
+class PagerIndicator : LinearLayout {
+
+ constructor(context: Context) : super(context)
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+ constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
+
+ private var selectedIndex = 0
+
+ /**
+ * Set the number of pager dots to display.
+ */
+ fun setSize(size: Int) {
+ if (childCount == size) {
+ return
+ }
+ if (selectedIndex >= size) {
+ selectedIndex = size - 1
+ }
+
+ removeAllViews()
+ for (i in 0 until size) {
+ val isLast = i == size - 1
+ addView(
+ View(context).apply {
+ setBackgroundResource(R.drawable.pager_dot)
+ isSelected = i == selectedIndex
+ },
+ LayoutParams(dpToPx(DOT_SIZE_IN_DP), dpToPx(DOT_SIZE_IN_DP)).apply {
+ if (!isLast) {
+ MarginLayoutParamsCompat.setMarginEnd(this, dpToPx(DOT_MARGIN))
+ }
+ }
+ )
+ }
+ }
+
+ /**
+ * Set the current selected pager dot.
+ */
+ fun setSelection(index: Int) {
+ if (selectedIndex == index) {
+ return
+ }
+
+ getChildAt(selectedIndex)?.run {
+ isSelected = false
+ }
+ getChildAt(index)?.run {
+ isSelected = true
+ }
+ selectedIndex = index
+ }
+
+ companion object {
+ private const val DOT_SIZE_IN_DP = 6f
+ private const val DOT_MARGIN = 4f
+ }
+}
+
+fun Context.dpToPx(value: Float): Int =
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, resources.displayMetrics).toInt()
+
+fun View.dpToPx(value: Float): Int = context.dpToPx(value)
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt
index 4f5ee6e1e..af5bb1de5 100644
--- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt
@@ -14,6 +14,8 @@ import kotlinx.android.synthetic.main.top_site_item.*
import mozilla.components.browser.menu.BrowserMenuBuilder
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
import mozilla.components.feature.top.sites.TopSite
+import mozilla.components.feature.top.sites.TopSite.Type.DEFAULT
+import mozilla.components.feature.top.sites.TopSite.Type.FRECENT
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.loadIntoView
@@ -26,23 +28,23 @@ class TopSiteItemViewHolder(
private val interactor: TopSiteInteractor
) : ViewHolder(view) {
private lateinit var topSite: TopSite
- private var topSiteMenu: TopSiteItemMenu
init {
- topSiteMenu = TopSiteItemMenu(view.context) {
- when (it) {
- is TopSiteItemMenu.Item.OpenInPrivateTab -> interactor.onOpenInPrivateTabClicked(
- topSite
- )
- is TopSiteItemMenu.Item.RemoveTopSite -> interactor.onRemoveTopSiteClicked(topSite)
- }
- }
-
top_site_item.setOnClickListener {
- interactor.onSelectTopSite(topSite.url, topSite.isDefault)
+ interactor.onSelectTopSite(topSite.url, topSite.type === DEFAULT)
}
top_site_item.setOnLongClickListener {
+ val topSiteMenu = TopSiteItemMenu(view.context, topSite.type != FRECENT) { item ->
+ when (item) {
+ is TopSiteItemMenu.Item.OpenInPrivateTab -> interactor.onOpenInPrivateTabClicked(
+ topSite
+ )
+ is TopSiteItemMenu.Item.RemoveTopSite -> interactor.onRemoveTopSiteClicked(
+ topSite
+ )
+ }
+ }
val menu = topSiteMenu.menuBuilder.build(view.context).show(anchor = it)
it.setOnTouchListener @SuppressLint("ClickableViewAccessibility") { v, event ->
onTouchEvent(v, event, menu)
@@ -82,6 +84,7 @@ class TopSiteItemViewHolder(
class TopSiteItemMenu(
private val context: Context,
+ private val isPinnedSite: Boolean,
private val onItemTapped: (Item) -> Unit = {}
) {
sealed class Item {
@@ -98,9 +101,12 @@ class TopSiteItemMenu(
) {
onItemTapped.invoke(Item.OpenInPrivateTab)
},
-
SimpleBrowserMenuItem(
- context.getString(R.string.remove_top_site)
+ if (isPinnedSite) {
+ context.getString(R.string.remove_top_site)
+ } else {
+ context.getString(R.string.delete_from_history)
+ }
) {
onItemTapped.invoke(Item.RemoveTopSite)
}
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSitesPagerAdapter.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSitesPagerAdapter.kt
new file mode 100644
index 000000000..b335e57de
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSitesPagerAdapter.kt
@@ -0,0 +1,40 @@
+/* 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.home.sessioncontrol.viewholders.topsites
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import mozilla.components.feature.top.sites.TopSite
+import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
+import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSiteViewHolder
+
+class TopSitesPagerAdapter(
+ private val interactor: TopSiteInteractor
+) : RecyclerView.Adapter() {
+
+ private var topSites: List> = listOf()
+
+ fun updateData(topSites: List) {
+ this.topSites = topSites.chunked(TOP_SITES_PER_PAGE)
+ notifyDataSetChanged()
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TopSiteViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(TopSiteViewHolder.LAYOUT_ID, parent, false)
+ return TopSiteViewHolder(view, interactor)
+ }
+
+ override fun onBindViewHolder(holder: TopSiteViewHolder, position: Int) {
+ holder.bind(this.topSites[position])
+ }
+
+ override fun getItemCount(): Int = this.topSites.size
+
+ companion object {
+ const val TOP_SITES_PER_PAGE = 8
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt
index a578b898f..27ecc25d4 100644
--- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt
@@ -21,6 +21,7 @@ import androidx.navigation.Navigation
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_edit_bookmark.*
+import kotlinx.android.synthetic.main.fragment_edit_bookmark.view.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
@@ -31,6 +32,7 @@ import mozilla.components.concept.storage.BookmarkNode
import mozilla.components.concept.storage.BookmarkNodeType
import mozilla.components.support.ktx.android.content.getColorFromAttr
import mozilla.components.support.ktx.android.view.hideKeyboard
+import mozilla.components.support.ktx.android.view.showKeyboard
import org.mozilla.fenix.NavHostActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar
@@ -38,6 +40,7 @@ import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getRootView
import org.mozilla.fenix.ext.nav
+import org.mozilla.fenix.ext.placeCursorAtEnd
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.setToolbarColors
import org.mozilla.fenix.ext.toShortUrl
@@ -107,6 +110,12 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) {
)
}
}
+
+ view.bookmarkNameEdit.apply {
+ requestFocus()
+ placeCursorAtEnd()
+ showKeyboard()
+ }
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderAdapter.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderAdapter.kt
index ad9aca6e9..d9064ef29 100644
--- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderAdapter.kt
+++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderAdapter.kt
@@ -8,6 +8,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
+import androidx.core.view.updatePaddingRelative
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@@ -86,7 +87,7 @@ class SelectBookmarkFolderAdapter(private val sharedViewModel: BookmarksSharedVi
}
val pxToIndent = dpsToIndent.dpToPx(view.context.resources.displayMetrics)
val padding = pxToIndent * if (folder.depth > maxDepth) maxDepth else folder.depth
- view.setPadding(padding, 0, 0, 0)
+ view.updatePaddingRelative(start = padding)
}
companion object {
diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadAdapter.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadAdapter.kt
new file mode 100644
index 000000000..71fc6d0f4
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadAdapter.kt
@@ -0,0 +1,40 @@
+/* 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.library.downloads
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import org.mozilla.fenix.library.SelectionHolder
+import org.mozilla.fenix.library.downloads.viewholders.DownloadsListItemViewHolder
+
+class DownloadAdapter(
+ private val downloadInteractor: DownloadInteractor
+) : RecyclerView.Adapter(), SelectionHolder {
+ private var downloads: List = listOf()
+ private var mode: DownloadFragmentState.Mode = DownloadFragmentState.Mode.Normal
+ override val selectedItems get() = mode.selectedItems
+
+ override fun getItemCount(): Int = downloads.size
+ override fun getItemViewType(position: Int): Int = DownloadsListItemViewHolder.LAYOUT_ID
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadsListItemViewHolder {
+ val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
+ return DownloadsListItemViewHolder(view, downloadInteractor, this)
+ }
+
+ fun updateMode(mode: DownloadFragmentState.Mode) {
+ this.mode = mode
+ }
+
+ override fun onBindViewHolder(holder: DownloadsListItemViewHolder, position: Int) {
+ holder.bind(downloads[position])
+ }
+
+ fun updateDownloads(downloads: List) {
+ this.downloads = downloads
+ notifyDataSetChanged()
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadController.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadController.kt
new file mode 100644
index 000000000..ccd30c136
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadController.kt
@@ -0,0 +1,30 @@
+/* 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.library.downloads
+
+import org.mozilla.fenix.browser.browsingmode.BrowsingMode
+
+interface DownloadController {
+ fun handleOpen(item: DownloadItem, mode: BrowsingMode? = null)
+ fun handleBackPressed(): Boolean
+}
+
+class DefaultDownloadController(
+ private val store: DownloadFragmentStore,
+ private val openToFileManager: (item: DownloadItem, mode: BrowsingMode?) -> Unit
+) : DownloadController {
+ override fun handleOpen(item: DownloadItem, mode: BrowsingMode?) {
+ openToFileManager(item, mode)
+ }
+
+ override fun handleBackPressed(): Boolean {
+ return if (store.state.mode is DownloadFragmentState.Mode.Editing) {
+ store.dispatch(DownloadFragmentAction.ExitEditMode)
+ true
+ } else {
+ false
+ }
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragment.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragment.kt
new file mode 100644
index 000000000..95effa563
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragment.kt
@@ -0,0 +1,113 @@
+/* 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.library.downloads
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import kotlinx.android.synthetic.main.fragment_downloads.view.*
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import mozilla.components.browser.state.state.content.DownloadState
+import mozilla.components.feature.downloads.AbstractFetchDownloadService
+import mozilla.components.lib.state.ext.consumeFrom
+import mozilla.components.support.base.feature.UserInteractionHandler
+import org.mozilla.fenix.HomeActivity
+import org.mozilla.fenix.R
+import org.mozilla.fenix.browser.browsingmode.BrowsingMode
+import org.mozilla.fenix.components.StoreProvider
+import org.mozilla.fenix.components.metrics.Event
+import org.mozilla.fenix.ext.filterNotExistsOnDisk
+import org.mozilla.fenix.ext.requireComponents
+import org.mozilla.fenix.ext.showToolbar
+import org.mozilla.fenix.library.LibraryPageFragment
+
+@SuppressWarnings("TooManyFunctions", "LargeClass")
+class DownloadFragment : LibraryPageFragment(), UserInteractionHandler {
+ private lateinit var downloadStore: DownloadFragmentStore
+ private lateinit var downloadView: DownloadView
+ private lateinit var downloadInteractor: DownloadInteractor
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val view = inflater.inflate(R.layout.fragment_downloads, container, false)
+
+ val items = requireComponents.core.store.state.downloads.map {
+ DownloadItem(
+ it.value.id.toString(),
+ it.value.fileName,
+ it.value.filePath,
+ it.value.contentLength.toString(),
+ it.value.contentType,
+ it.value.status
+ )
+ }.filter {
+ it.status == DownloadState.Status.COMPLETED
+ }.filterNotExistsOnDisk()
+
+ downloadStore = StoreProvider.get(this) {
+ DownloadFragmentStore(
+ DownloadFragmentState(
+ items = items,
+ mode = DownloadFragmentState.Mode.Normal
+ )
+ )
+ }
+
+ val downloadController: DownloadController = DefaultDownloadController(
+ downloadStore,
+ ::openItem
+ )
+ downloadInteractor = DownloadInteractor(
+ downloadController
+ )
+ downloadView = DownloadView(view.downloadsLayout, downloadInteractor)
+
+ return view
+ }
+
+ override val selectedItems get() = downloadStore.state.mode.selectedItems
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ requireComponents.analytics.metrics.track(Event.HistoryOpened)
+
+ setHasOptionsMenu(false)
+ }
+
+ @ExperimentalCoroutinesApi
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ consumeFrom(downloadStore) {
+ downloadView.update(it)
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ showToolbar(getString(R.string.library_downloads))
+ }
+
+ override fun onBackPressed(): Boolean {
+ return downloadView.onBackPressed()
+ }
+
+ private fun openItem(item: DownloadItem, mode: BrowsingMode? = null) {
+
+ mode?.let { (activity as HomeActivity).browsingModeManager.mode = it }
+ context?.let {
+ AbstractFetchDownloadService.openFile(
+ context = it,
+ contentType = item.contentType,
+ filePath = item.filePath
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragmentStore.kt
new file mode 100644
index 000000000..8f4915e33
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadFragmentStore.kt
@@ -0,0 +1,69 @@
+/* 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.library.downloads
+
+import mozilla.components.browser.state.state.content.DownloadState
+import mozilla.components.lib.state.Action
+import mozilla.components.lib.state.State
+import mozilla.components.lib.state.Store
+
+/**
+ * Class representing a history entry
+ * @property id Unique id of the download item
+ * @property fileName File name of the download item
+ * @property filePath Full path of the download item
+ * @property size The size in bytes of the download item
+ * @property contentType The type of file the download is
+ */
+data class DownloadItem(
+ val id: String,
+ val fileName: String?,
+ val filePath: String,
+ val size: String,
+ val contentType: String?,
+ val status: DownloadState.Status
+)
+
+/**
+ * The [Store] for holding the [DownloadFragmentState] and applying [DownloadFragmentAction]s.
+ */
+class DownloadFragmentStore(initialState: DownloadFragmentState) :
+ Store(initialState, ::downloadStateReducer)
+
+/**
+ * Actions to dispatch through the `DownloadStore` to modify `DownloadState` through the reducer.
+ */
+sealed class DownloadFragmentAction : Action {
+ object ExitEditMode : DownloadFragmentAction()
+}
+
+/**
+ * The state for the Download Screen
+ * @property items List of DownloadItem to display
+ * @property mode Current Mode of Download
+ */
+data class DownloadFragmentState(
+ val items: List,
+ val mode: Mode
+) : State {
+ sealed class Mode {
+ open val selectedItems = emptySet()
+
+ object Normal : Mode()
+ data class Editing(override val selectedItems: Set) : DownloadFragmentState.Mode()
+ }
+}
+
+/**
+ * The DownloadState Reducer.
+ */
+private fun downloadStateReducer(
+ state: DownloadFragmentState,
+ action: DownloadFragmentAction
+): DownloadFragmentState {
+ return when (action) {
+ is DownloadFragmentAction.ExitEditMode -> state.copy(mode = DownloadFragmentState.Mode.Normal)
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadInteractor.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadInteractor.kt
new file mode 100644
index 000000000..ea55bd2eb
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadInteractor.kt
@@ -0,0 +1,25 @@
+/* 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.library.downloads
+/**
+ * Interactor for the download screen
+ * Provides implementations for the DownloadViewInteractor
+ */
+@SuppressWarnings("TooManyFunctions")
+class DownloadInteractor(
+ private val downloadController: DownloadController
+) : DownloadViewInteractor {
+ override fun open(item: DownloadItem) {
+ downloadController.handleOpen(item)
+ }
+
+ override fun select(item: DownloadItem) { /* noop */ }
+
+ override fun deselect(item: DownloadItem) { /* noop */ }
+
+ override fun onBackPressed(): Boolean {
+ return downloadController.handleBackPressed()
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadView.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadView.kt
new file mode 100644
index 000000000..76989458d
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/library/downloads/DownloadView.kt
@@ -0,0 +1,83 @@
+/* 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.library.downloads
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.SimpleItemAnimator
+import kotlinx.android.synthetic.main.component_downloads.*
+import kotlinx.android.synthetic.main.component_downloads.view.*
+import mozilla.components.support.base.feature.UserInteractionHandler
+import org.mozilla.fenix.R
+import org.mozilla.fenix.library.LibraryPageView
+import org.mozilla.fenix.library.SelectionInteractor
+
+/**
+ * Interface for the DownloadViewInteractor. This interface is implemented by objects that want
+ * to respond to user interaction on the DownloadView
+ */
+interface DownloadViewInteractor : SelectionInteractor {
+
+ /**
+ * Called on backpressed to exit edit mode
+ */
+ fun onBackPressed(): Boolean
+}
+
+/**
+ * View that contains and configures the Downloads List
+ */
+class DownloadView(
+ container: ViewGroup,
+ val interactor: DownloadInteractor
+) : LibraryPageView(container), UserInteractionHandler {
+
+ val view: View = LayoutInflater.from(container.context)
+ .inflate(R.layout.component_downloads, container, true)
+
+ var mode: DownloadFragmentState.Mode = DownloadFragmentState.Mode.Normal
+ private set
+
+ val downloadAdapter = DownloadAdapter(interactor)
+ private val layoutManager = LinearLayoutManager(container.context)
+
+ init {
+ view.download_list.apply {
+ layoutManager = this@DownloadView.layoutManager
+ adapter = downloadAdapter
+ (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
+ }
+ }
+
+ fun update(state: DownloadFragmentState) {
+
+ view.swipe_refresh.isEnabled = false
+ mode = state.mode
+
+ updateEmptyState(state.items.isNotEmpty())
+
+ downloadAdapter.updateMode(state.mode)
+ downloadAdapter.updateDownloads(state.items)
+
+ setUiForNormalMode(
+ context.getString(R.string.library_downloads)
+ )
+ }
+
+ fun updateEmptyState(userHasDownloads: Boolean) {
+ download_list.isVisible = userHasDownloads
+ download_empty_view.isVisible = !userHasDownloads
+ if (!userHasDownloads) {
+ download_empty_view.announceForAccessibility(context.getString(R.string.download_empty_message))
+ }
+ }
+
+ override fun onBackPressed(): Boolean {
+ return interactor.onBackPressed()
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/library/downloads/viewholders/DownloadsListItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/downloads/viewholders/DownloadsListItemViewHolder.kt
new file mode 100644
index 000000000..c367ca674
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/library/downloads/viewholders/DownloadsListItemViewHolder.kt
@@ -0,0 +1,47 @@
+/* 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.library.downloads.viewholders
+
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+import kotlinx.android.synthetic.main.download_list_item.view.*
+import kotlinx.android.synthetic.main.library_site_item.view.*
+import org.mozilla.fenix.R
+import org.mozilla.fenix.ext.hideAndDisable
+import org.mozilla.fenix.library.SelectionHolder
+import org.mozilla.fenix.library.downloads.DownloadInteractor
+import org.mozilla.fenix.library.downloads.DownloadItem
+import mozilla.components.feature.downloads.toMegabyteString
+import org.mozilla.fenix.ext.getIcon
+
+class DownloadsListItemViewHolder(
+ view: View,
+ private val downloadInteractor: DownloadInteractor,
+ private val selectionHolder: SelectionHolder
+) : RecyclerView.ViewHolder(view) {
+
+ private var item: DownloadItem? = null
+
+ fun bind(
+ item: DownloadItem
+ ) {
+ itemView.download_layout.visibility = View.VISIBLE
+ itemView.download_layout.titleView.text = item.fileName
+ itemView.download_layout.urlView.text = item.size.toLong().toMegabyteString()
+
+ itemView.download_layout.setSelectionInteractor(item, selectionHolder, downloadInteractor)
+ itemView.download_layout.changeSelected(item in selectionHolder.selectedItems)
+
+ itemView.overflow_menu.hideAndDisable()
+ itemView.favicon.setImageResource(item.getIcon())
+ itemView.favicon.isClickable = false
+
+ this.item = item
+ }
+
+ companion object {
+ const val LAYOUT_ID = R.layout.download_list_item
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/migration/MigrationProgressActivity.kt b/app/src/main/java/org/mozilla/fenix/migration/MigrationProgressActivity.kt
index 16503f283..92be23ec6 100644
--- a/app/src/main/java/org/mozilla/fenix/migration/MigrationProgressActivity.kt
+++ b/app/src/main/java/org/mozilla/fenix/migration/MigrationProgressActivity.kt
@@ -59,7 +59,7 @@ class MigrationProgressActivity : AbstractMigrationProgressActivity() {
override fun onMigrationCompleted(results: MigrationResults) {
// Enable clicking the finish button
- migration_button_text_view.apply {
+ migration_button.apply {
setOnClickListener {
AbstractMigrationService.dismissNotification(context)
@@ -78,6 +78,8 @@ class MigrationProgressActivity : AbstractMigrationProgressActivity() {
startActivity(Intent(this@MigrationProgressActivity, HomeActivity::class.java))
}
}
+ }
+ migration_button_text_view.apply {
text = getString(R.string.migration_update_app_button, getString(R.string.app_name))
setTextColor(ContextCompat.getColor(context, R.color.white_color))
}
diff --git a/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingController.kt b/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingController.kt
new file mode 100644
index 000000000..3e1945103
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingController.kt
@@ -0,0 +1,22 @@
+/* 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.onboarding
+
+import android.content.Context
+import org.mozilla.fenix.BrowserDirection
+import org.mozilla.fenix.HomeActivity
+import org.mozilla.fenix.settings.SupportUtils
+
+class OnboardingController(
+ private val context: Context
+) {
+ fun handleLearnMoreClicked() {
+ (context as HomeActivity).openToBrowserAndLoad(
+ searchTermOrURL = SupportUtils.getFirefoxAccountSumoUrl(),
+ newTab = true,
+ from = BrowserDirection.FromHome
+ )
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingInteractor.kt b/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingInteractor.kt
new file mode 100644
index 000000000..ad11d459c
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingInteractor.kt
@@ -0,0 +1,14 @@
+/* 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.onboarding
+
+class OnboardingInteractor(private val onboardingController: OnboardingController) {
+
+ /**
+ * Called when the user clicks the learn more link
+ * @param url the url the suggestion was providing
+ */
+ fun onLearnMoreClicked() = onboardingController.handleLearnMoreClicked()
+}
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 4ed8d330c..6fdb7d0af 100644
--- a/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingRadioButton.kt
+++ b/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingRadioButton.kt
@@ -71,14 +71,14 @@ class OnboardingRadioButton(
val spannableTitle = SpannableString(resources.getString(title))
spannableTitle.setTextSize(context, TITLE_TEXT_SIZE)
- spannableTitle.setTextColor(context, R.color.primary_state_list_text_color)
+ spannableTitle.setTextColor(context, R.attr.primaryText)
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)
+ spannableDescription.setTextColor(context, R.attr.secondaryText)
builder.append("\n")
builder.append(spannableDescription)
}
diff --git a/app/src/main/java/org/mozilla/fenix/perf/VisualCompletenessQueue.kt b/app/src/main/java/org/mozilla/fenix/perf/VisualCompletenessQueue.kt
new file mode 100644
index 000000000..2dd88c289
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/perf/VisualCompletenessQueue.kt
@@ -0,0 +1,33 @@
+/* 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.perf
+
+import android.view.View
+import androidx.core.view.doOnPreDraw
+import mozilla.components.support.utils.RunWhenReadyQueue
+import java.lang.ref.WeakReference
+
+/**
+ * class for all functionality related to Visual completeness queue
+ */
+class VisualCompletenessQueue(val queue: RunWhenReadyQueue) {
+ @Suppress("MagicNumber")
+ val delay = 5000L
+
+ /**
+ *
+ * @param containerWeakReference a weak reference to the root view of a view hierarchy. Weak
+ * reference is to avoid memory leak.
+ */
+ fun attachViewToRunVisualCompletenessQueueLater(containerWeakReference: WeakReference) {
+ containerWeakReference.get()?.doOnPreDraw {
+ // This delay is temporary. We are delaying 5 seconds until the performance
+ // team can locate the real point of visual completeness.
+ it.postDelayed({
+ queue.ready()
+ }, delay)
+ }
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchController.kt b/app/src/main/java/org/mozilla/fenix/search/SearchController.kt
index b2fe99221..8e668c7ea 100644
--- a/app/src/main/java/org/mozilla/fenix/search/SearchController.kt
+++ b/app/src/main/java/org/mozilla/fenix/search/SearchController.kt
@@ -61,6 +61,10 @@ class DefaultSearchController(
// and open the crash list activity instead.
activity.startActivity(Intent(activity, CrashListActivity::class.java))
}
+ "about:addons" -> {
+ val directions = SearchFragmentDirections.actionGlobalAddonsManagementFragment()
+ navController.navigateSafe(R.id.searchFragment, directions)
+ }
"moz://a" -> openSearchOrUrl(SupportUtils.getMozillaPageUrl(MANIFESTO))
else -> if (url.isNotBlank()) {
openSearchOrUrl(url)
diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt
index d407a8d91..955dc0844 100644
--- a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt
@@ -28,7 +28,7 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_search.*
import kotlinx.android.synthetic.main.fragment_search.view.*
-import kotlinx.android.synthetic.main.search_suggestions_onboarding.view.*
+import kotlinx.android.synthetic.main.search_suggestions_hint.view.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.concept.storage.HistoryStorage
diff --git a/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogController.kt b/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogController.kt
index 3585efbbf..c4e81c041 100644
--- a/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogController.kt
+++ b/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogController.kt
@@ -44,6 +44,10 @@ class SearchDialogController(
// and open the crash list activity instead.
activity.startActivity(Intent(activity, CrashListActivity::class.java))
}
+ "about:addons" -> {
+ val directions = SearchDialogFragmentDirections.actionGlobalAddonsManagementFragment()
+ navController.navigateSafe(R.id.searchDialogFragment, directions)
+ }
"moz://a" -> openSearchOrUrl(SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.MANIFESTO))
else -> if (url.isNotBlank()) {
openSearchOrUrl(url)
@@ -159,6 +163,7 @@ class SearchDialogController(
}
override fun handleClickSearchEngineSettings() {
+ clearToolbarFocus()
val directions = SearchDialogFragmentDirections.actionGlobalSearchEngineFragment()
navController.navigateSafe(R.id.searchDialogFragment, directions)
}
diff --git a/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt
index 9c869aa81..7d0454ba8 100644
--- a/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt
@@ -4,44 +4,80 @@
package org.mozilla.fenix.searchdialog
+import android.app.Activity
import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.content.Intent
+import android.graphics.Typeface
import android.os.Bundle
+import android.speech.RecognizerIntent
+import android.text.style.StyleSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.view.ViewStub
+import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.constraintlayout.widget.ConstraintProperties.BOTTOM
import androidx.constraintlayout.widget.ConstraintProperties.PARENT_ID
import androidx.constraintlayout.widget.ConstraintProperties.TOP
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
-import kotlinx.android.synthetic.main.fragment_search.view.*
import kotlinx.android.synthetic.main.fragment_search_dialog.*
+import kotlinx.android.synthetic.main.fragment_search_dialog.fill_link_from_clipboard
+import kotlinx.android.synthetic.main.fragment_search_dialog.pill_wrapper
+import kotlinx.android.synthetic.main.fragment_search_dialog.qr_scan_button
+import kotlinx.android.synthetic.main.fragment_search_dialog.toolbar
+import kotlinx.android.synthetic.main.fragment_search_dialog.view.*
+import kotlinx.android.synthetic.main.search_suggestions_hint.view.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import mozilla.components.browser.toolbar.BrowserToolbar
+import mozilla.components.feature.qr.QrFeature
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.UserInteractionHandler
+import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
+import mozilla.components.support.ktx.android.content.hasCamera
+import mozilla.components.support.ktx.android.content.res.getSpanned
import mozilla.components.support.ktx.android.view.hideKeyboard
+import mozilla.components.ui.autocomplete.InlineAutocompleteEditText
+import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
+import org.mozilla.fenix.components.metrics.Event
+import org.mozilla.fenix.components.searchengine.CustomSearchEngineStore
+import org.mozilla.fenix.components.searchengine.FenixSearchEngineProvider
import org.mozilla.fenix.components.toolbar.ToolbarPosition
+import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
+import org.mozilla.fenix.search.SearchFragmentAction
+import org.mozilla.fenix.search.SearchFragmentState
import org.mozilla.fenix.search.SearchFragmentStore
import org.mozilla.fenix.search.SearchInteractor
import org.mozilla.fenix.search.awesomebar.AwesomeBarView
import org.mozilla.fenix.search.createInitialSearchFragmentState
import org.mozilla.fenix.search.toolbar.ToolbarView
+import org.mozilla.fenix.settings.SupportUtils
+import org.mozilla.fenix.settings.registerOnSharedPreferenceChangeListener
+import org.mozilla.fenix.widget.VoiceSearchActivity
typealias SearchDialogFragmentStore = SearchFragmentStore
typealias SearchDialogInteractor = SearchInteractor
+@SuppressWarnings("LargeClass", "TooManyFunctions")
class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
-
private lateinit var interactor: SearchDialogInteractor
private lateinit var store: SearchDialogFragmentStore
private lateinit var toolbarView: ToolbarView
private lateinit var awesomeBarView: AwesomeBarView
+ private var firstUpdate = true
+
+ private val qrFeature = ViewBoundFeatureWrapper()
+ private val speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -64,6 +100,8 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
val args by navArgs()
val view = inflater.inflate(R.layout.fragment_search_dialog, container, false)
+ requireComponents.analytics.metrics.track(Event.InteractWithSearchURLArea)
+
store = SearchDialogFragmentStore(
createInitialSearchFragmentState(
activity as HomeActivity,
@@ -96,21 +134,196 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
false,
view.toolbar,
requireComponents.core.engine
- )
+ ).also(::addSearchButton)
awesomeBarView = AwesomeBarView(
requireContext(),
interactor,
- view.awesomeBar
+ view.awesome_bar
)
+ setShortcutsChangedListener(CustomSearchEngineStore.PREF_FILE_SEARCH_ENGINES)
+ setShortcutsChangedListener(FenixSearchEngineProvider.PREF_FILE_SEARCH_ENGINES)
+
+ view.awesome_bar.setOnTouchListener { _, _ ->
+ view.hideKeyboard()
+ false
+ }
+
+ awesomeBarView.view.setOnEditSuggestionListener(toolbarView.view::setSearchTerms)
+
+ val urlView = toolbarView.view
+ .findViewById(R.id.mozac_browser_toolbar_edit_url_view)
+ urlView?.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+
+ val isPrivate = (requireActivity() as HomeActivity).browsingModeManager.mode.isPrivate
+ requireComponents.core.engine.speculativeCreateSession(isPrivate)
+
return view
}
@ExperimentalCoroutinesApi
+ @SuppressWarnings("LongMethod")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ setupConstraints(view)
+
+ search_wrapper.setOnClickListener {
+ it.hideKeyboard()
+ dismissAllowingStateLoss()
+ }
+
+ view.search_engines_shortcut_button.setOnClickListener {
+ interactor.onSearchShortcutsButtonClicked()
+ }
+
+ qr_scan_button.visibility = if (context?.hasCamera() == true) View.VISIBLE else View.GONE
+
+ qr_scan_button.setOnClickListener {
+ if (!requireContext().hasCamera()) { return@setOnClickListener }
+
+ toolbarView.view.clearFocus()
+ requireComponents.analytics.metrics.track(Event.QRScannerOpened)
+ qrFeature.get()?.scan(R.id.search_wrapper)
+ }
+
+ fill_link_from_clipboard.setOnClickListener {
+ (activity as HomeActivity)
+ .openToBrowserAndLoad(
+ searchTermOrURL = requireContext().components.clipboardHandler.url ?: "",
+ newTab = store.state.tabId == null,
+ from = BrowserDirection.FromSearchDialog
+ )
+ }
+
+ qrFeature.set(
+ createQrFeature(),
+ owner = this,
+ view = view
+ )
+
+ val stubListener = ViewStub.OnInflateListener { _, inflated ->
+ inflated.learn_more.setOnClickListener {
+ (activity as HomeActivity)
+ .openToBrowserAndLoad(
+ searchTermOrURL = SupportUtils.getGenericSumoURLForTopic(
+ SupportUtils.SumoTopic.SEARCH_SUGGESTION
+ ),
+ newTab = store.state.tabId == null,
+ from = BrowserDirection.FromSearch
+ )
+ }
+
+ inflated.allow.setOnClickListener {
+ inflated.visibility = View.GONE
+ requireContext().settings().also {
+ it.shouldShowSearchSuggestionsInPrivate = true
+ it.showSearchSuggestionsInPrivateOnboardingFinished = true
+ }
+ store.dispatch(SearchFragmentAction.SetShowSearchSuggestions(true))
+ store.dispatch(SearchFragmentAction.AllowSearchSuggestionsInPrivateModePrompt(false))
+ requireComponents.analytics.metrics.track(Event.PrivateBrowsingShowSearchSuggestions)
+ }
+
+ inflated.dismiss.setOnClickListener {
+ inflated.visibility = View.GONE
+ requireContext().settings().also {
+ it.shouldShowSearchSuggestionsInPrivate = false
+ it.showSearchSuggestionsInPrivateOnboardingFinished = true
+ }
+ }
+
+ inflated.text.text =
+ getString(R.string.search_suggestions_onboarding_text, getString(R.string.app_name))
+
+ inflated.title.text =
+ getString(R.string.search_suggestions_onboarding_title)
+ }
+
+ view.search_suggestions_hint.setOnInflateListener((stubListener))
+
+ consumeFrom(store) {
+ val shouldShowAwesomebar =
+ !firstUpdate &&
+ it.query.isNotBlank() ||
+ it.showSearchShortcuts
+
+ awesome_bar?.visibility = if (shouldShowAwesomebar) View.VISIBLE else View.INVISIBLE
+ updateSearchSuggestionsHintVisibility(it)
+ updateClipboardSuggestion(it, requireContext().components.clipboardHandler.url)
+ updateToolbarContentDescription(it)
+ toolbarView.update(it)
+ awesomeBarView.update(it)
+ firstUpdate = false
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
+ if (requestCode == VoiceSearchActivity.SPEECH_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+ intent?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)?.first()?.also {
+ toolbarView.view.edit.updateUrl(url = it, shouldHighlight = true)
+ interactor.onTextChanged(it)
+ toolbarView.view.edit.focus()
+ }
+ }
+ }
+
+ override fun onBackPressed(): Boolean {
+ return when {
+ qrFeature.onBackPressed() -> {
+ toolbarView.view.edit.focus()
+ view?.qr_scan_button?.isChecked = false
+ toolbarView.view.requestFocus()
+ true
+ }
+ else -> {
+ view?.hideKeyboard()
+ dismissAllowingStateLoss()
+ true
+ }
+ }
+ }
+
+ private fun createQrFeature(): QrFeature {
+ return QrFeature(
+ requireContext(),
+ fragmentManager = childFragmentManager,
+ onNeedToRequestPermissions = { permissions ->
+ requestPermissions(permissions, REQUEST_CODE_CAMERA_PERMISSIONS)
+ },
+ onScanResult = { result ->
+ qr_scan_button.isChecked = false
+ activity?.let {
+ AlertDialog.Builder(it).apply {
+ val spannable = resources.getSpanned(
+ R.string.qr_scanner_confirmation_dialog_message,
+ getString(R.string.app_name) to StyleSpan(Typeface.BOLD),
+ result to StyleSpan(Typeface.ITALIC)
+ )
+ setMessage(spannable)
+ setNegativeButton(R.string.qr_scanner_dialog_negative) { dialog: DialogInterface, _ ->
+ requireComponents.analytics.metrics.track(Event.QRScannerNavigationDenied)
+ dialog.cancel()
+ }
+ setPositiveButton(R.string.qr_scanner_dialog_positive) { dialog: DialogInterface, _ ->
+ requireComponents.analytics.metrics.track(Event.QRScannerNavigationAllowed)
+ (activity as HomeActivity)
+ .openToBrowserAndLoad(
+ searchTermOrURL = result,
+ newTab = store.state.tabId == null,
+ from = BrowserDirection.FromSearch
+ )
+ dialog.dismiss()
+ }
+ create()
+ }.show()
+ requireComponents.analytics.metrics.track(Event.QRScannerPromptDisplayed)
+ }
+ })
+ }
+
+ private fun setupConstraints(view: View) {
if (view.context.settings().toolbarPosition == ToolbarPosition.BOTTOM) {
ConstraintSet().apply {
clone(search_wrapper)
@@ -118,31 +331,91 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
clear(toolbar.id, TOP)
connect(toolbar.id, BOTTOM, PARENT_ID, BOTTOM)
- clear(awesomeBar.id, TOP)
- clear(awesomeBar.id, BOTTOM)
- connect(awesomeBar.id, TOP, PARENT_ID, TOP)
- connect(awesomeBar.id, BOTTOM, toolbar.id, TOP)
+ clear(awesome_bar.id, TOP)
+ clear(pill_wrapper.id, BOTTOM)
+ connect(awesome_bar.id, TOP, PARENT_ID, TOP)
+ connect(pill_wrapper.id, BOTTOM, toolbar.id, TOP)
+
+ clear(fill_link_from_clipboard.id, TOP)
+ connect(fill_link_from_clipboard.id, BOTTOM, pill_wrapper.id, TOP)
applyTo(search_wrapper)
}
}
+ }
- search_wrapper.setOnClickListener {
- it.hideKeyboard()
- dismissAllowingStateLoss()
+ private fun updateSearchSuggestionsHintVisibility(state: SearchFragmentState) {
+ view?.apply {
+ val showHint = state.showSearchSuggestionsHint && !state.showSearchShortcuts
+ findViewById(R.id.search_suggestions_hint)?.isVisible = showHint
+ search_suggestions_hint_divider?.isVisible = showHint
}
+ }
- consumeFrom(store) {
- awesomeBar?.visibility = if (it.query.isEmpty()) View.INVISIBLE else View.VISIBLE
- toolbarView.update(it)
- awesomeBarView.update(it)
+ private fun addSearchButton(toolbarView: ToolbarView) {
+ toolbarView.view.addEditAction(
+ BrowserToolbar.Button(
+ ContextCompat.getDrawable(requireContext(), R.drawable.ic_microphone)!!,
+ requireContext().getString(R.string.voice_search_content_description),
+ visible = {
+ store.state.searchEngineSource.searchEngine.identifier.contains("google") &&
+ isSpeechAvailable() &&
+ requireContext().settings().shouldShowVoiceSearch
+ },
+ listener = ::launchVoiceSearch
+ )
+ )
+ }
+
+ private fun launchVoiceSearch() {
+ // Note if a user disables speech while the app is on the search fragment
+ // the voice button will still be available and *will* cause a crash if tapped,
+ // since the `visible` call is only checked on create. In order to avoid extra complexity
+ // around such a small edge case, we make the button have no functionality in this case.
+ if (!isSpeechAvailable()) { return }
+
+ requireComponents.analytics.metrics.track(Event.VoiceSearchTapped)
+ speechIntent.apply {
+ putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
+ putExtra(RecognizerIntent.EXTRA_PROMPT, requireContext().getString(R.string.voice_search_explainer))
}
+ startActivityForResult(speechIntent, VoiceSearchActivity.SPEECH_REQUEST_CODE)
}
- override fun onBackPressed(): Boolean {
- view?.hideKeyboard()
- dismissAllowingStateLoss()
+ private fun isSpeechAvailable(): Boolean = speechIntent.resolveActivity(requireContext().packageManager) != null
+
+ private fun setShortcutsChangedListener(preferenceFileName: String) {
+ requireContext().getSharedPreferences(
+ preferenceFileName,
+ Context.MODE_PRIVATE
+ ).registerOnSharedPreferenceChangeListener(viewLifecycleOwner) { _, _ ->
+ awesomeBarView.update(store.state)
+ }
+ }
+
+ private fun updateClipboardSuggestion(searchState: SearchFragmentState, clipboardUrl: String?) {
+ val shouldShowView = searchState.showClipboardSuggestions &&
+ searchState.query.isEmpty() &&
+ !clipboardUrl.isNullOrEmpty() &&
+ !searchState.showSearchShortcuts
+
+ fill_link_from_clipboard.visibility = if (shouldShowView) View.VISIBLE else View.GONE
+ clipboard_url.text = clipboardUrl
+
+ if (clipboardUrl != null && !((activity as HomeActivity).browsingModeManager.mode.isPrivate)) {
+ requireComponents.core.engine.speculativeConnect(clipboardUrl)
+ }
+ }
+
+ private fun updateToolbarContentDescription(searchState: SearchFragmentState) {
+ val urlView = toolbarView.view
+ .findViewById(R.id.mozac_browser_toolbar_edit_url_view)
+ toolbarView.view.contentDescription =
+ searchState.searchEngineSource.searchEngine.name + ", " + urlView.hint
+ urlView?.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ }
- return true
+ companion object {
+ private const val REQUEST_CODE_CAMERA_PERMISSIONS = 1
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/session/PerformanceActivityLifecycleCallbacks.kt b/app/src/main/java/org/mozilla/fenix/session/PerformanceActivityLifecycleCallbacks.kt
index 2684b0388..6f46fb997 100644
--- a/app/src/main/java/org/mozilla/fenix/session/PerformanceActivityLifecycleCallbacks.kt
+++ b/app/src/main/java/org/mozilla/fenix/session/PerformanceActivityLifecycleCallbacks.kt
@@ -51,7 +51,7 @@ class PerformanceActivityLifecycleCallbacks(
if (activity is HomeActivity) {
// We should delay the visualCompletenessQueue when reaching the HomeActivity
// to ensure all tasks are delayed until after visual completeness
- activity.postVisualCompletenessQueue(visualCompletenessQueue)
+ activity.setVisualCompletenessQueueReady()
} else if (shouldStartVisualCompletenessQueueImmediately()) {
// If we do not go through the home activity, we have to start the tasks
// immediately to avoid spending time implementing it.
diff --git a/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt
index 4686a04a7..bdf658647 100644
--- a/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt
@@ -9,8 +9,10 @@ import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import androidx.appcompat.app.AppCompatDelegate
+import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
+import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.toolbar.ToolbarPosition
@@ -54,6 +56,7 @@ class CustomizationFragment : PreferenceFragmentCompat() {
bindAutoBatteryTheme()
setupRadioGroups()
setupToolbarCategory()
+ setupHomeCategory()
}
private fun setupRadioGroups() {
@@ -136,4 +139,15 @@ class CustomizationFragment : PreferenceFragmentCompat() {
addToRadioGroup(topPreference, bottomPreference)
}
+
+ private fun setupHomeCategory() {
+ requirePreference(R.string.pref_home_category).apply {
+ isVisible = FeatureFlags.topFrecentSite
+ }
+ requirePreference(R.string.pref_key_enable_top_frecent_sites).apply {
+ isVisible = FeatureFlags.topFrecentSite
+ isChecked = context.settings().showTopFrecentSites
+ onPreferenceChangeListener = SharedPreferenceUpdater()
+ }
+ }
}
diff --git a/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt
index c3e663104..0d15672bd 100644
--- a/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt
@@ -5,8 +5,14 @@
package org.mozilla.fenix.settings
import android.os.Bundle
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
+import com.google.android.play.core.ktx.launchReview
+import com.google.android.play.core.ktx.requestReview
+import com.google.android.play.core.review.ReviewManagerFactory
+import kotlinx.coroutines.launch
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.settings
@@ -25,9 +31,9 @@ class SecretSettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.secret_settings_preferences, rootKey)
- requirePreference(R.string.pref_key_use_new_search_experience).apply {
- isVisible = FeatureFlags.newSearchExperience
- isChecked = context.settings().useNewSearchExperience
+ requirePreference(R.string.pref_key_enable_top_frecent_sites).apply {
+ isVisible = FeatureFlags.topFrecentSite
+ isChecked = context.settings().showTopFrecentSites
onPreferenceChangeListener = SharedPreferenceUpdater()
}
@@ -36,5 +42,22 @@ class SecretSettingsFragment : PreferenceFragmentCompat() {
isChecked = context.settings().waitToShowPageUntilFirstPaint
onPreferenceChangeListener = SharedPreferenceUpdater()
}
+
+ requirePreference(R.string.pref_key_synced_tabs_tabs_tray).apply {
+ isVisible = FeatureFlags.syncedTabsInTabsTray
+ isChecked = context.settings().syncedTabsInTabsTray
+ onPreferenceChangeListener = SharedPreferenceUpdater()
+ }
+
+ requirePreference(R.string.pref_key_temp_review_prompt).apply {
+ setOnPreferenceClickListener {
+ viewLifecycleOwner.lifecycleScope.launch {
+ val manager = ReviewManagerFactory.create(requireContext())
+ val reviewInfo = manager.requestReview()
+ manager.launchReview(requireActivity(), reviewInfo)
+ }
+ true
+ }
+ }
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt
index f74e27bb4..86c117bd2 100644
--- a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt
@@ -25,7 +25,6 @@ import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.concept.sync.Profile
-import mozilla.components.support.ktx.android.content.hasCamera
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.Config
import org.mozilla.fenix.HomeActivity
@@ -36,6 +35,7 @@ import org.mozilla.fenix.ext.application
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getPreferenceKey
import org.mozilla.fenix.ext.metrics
+import org.mozilla.fenix.ext.navigateToNotificationsSettings
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
@@ -190,16 +190,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
val directions: NavDirections? = when (preference.key) {
resources.getString(R.string.pref_key_sign_in) -> {
- // App can be installed on devices with no camera modules. Like Android TV boxes.
- // Let's skip presenting the option to sign in by scanning a qr code in this case
- // and default to login with email and password.
- if (requireContext().hasCamera()) {
- SettingsFragmentDirections.actionSettingsFragmentToTurnOnSyncFragment()
- } else {
- requireComponents.services.accountsAuthFeature.beginAuthentication(requireContext())
- requireComponents.analytics.metrics.track(Event.SyncAuthUseEmail)
- null
- }
+ SettingsFragmentDirections.actionSettingsFragmentToTurnOnSyncFragment()
}
resources.getString(R.string.pref_key_search_settings) -> {
SettingsFragmentDirections.actionSettingsFragmentToSearchEngineFragment()
@@ -270,6 +261,10 @@ class SettingsFragment : PreferenceFragmentCompat() {
resources.getString(R.string.pref_key_delete_browsing_data_on_quit_preference) -> {
SettingsFragmentDirections.actionSettingsFragmentToDeleteBrowsingDataOnQuitFragment()
}
+ resources.getString(R.string.pref_key_notifications) -> {
+ context?.navigateToNotificationsSettings()
+ null
+ }
resources.getString(R.string.pref_key_customize) -> {
SettingsFragmentDirections.actionSettingsFragmentToCustomizationFragment()
}
diff --git a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt
index a1a613cc1..3e1bae04a 100644
--- a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt
+++ b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt
@@ -74,6 +74,10 @@ object SupportUtils {
return "https://support.mozilla.org/$langTag/kb/$escapedTopic"
}
+ fun getFirefoxAccountSumoUrl(): String {
+ return "https://support.mozilla.org/kb/access-mozilla-services-firefox-account"
+ }
+
fun getMozillaPageUrl(page: MozillaPage, locale: Locale = Locale.getDefault()): String {
val path = page.path
val langTag = getLanguageTag(locale)
diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt
index 3fd152943..8f01a1e3b 100644
--- a/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt
@@ -12,6 +12,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.content.pm.PackageInfoCompat
import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration
import kotlinx.android.synthetic.main.fragment_about.*
import org.mozilla.fenix.BrowserDirection
@@ -167,8 +168,8 @@ class AboutFragment : Fragment(), AboutPageListener {
}
private fun openLibrariesPage() {
- val intent = Intent(requireContext(), AboutLibrariesActivity::class.java)
- startActivity(intent)
+ val navController = findNavController()
+ navController.navigate(R.id.action_aboutFragment_to_aboutLibrariesFragment)
}
override fun onAboutItemClicked(item: AboutItem) {
@@ -185,18 +186,12 @@ class AboutFragment : Fragment(), AboutPageListener {
PRIVACY_NOTICE -> {
requireComponents.analytics.metrics.track(Event.PrivacyNoticeTapped)
}
- RIGHTS -> {
- requireComponents.analytics.metrics.track(Event.RightsTapped)
- }
- LICENSING_INFO -> {
- requireComponents.analytics.metrics.track(Event.LicensingTapped)
- }
+ LICENSING_INFO, RIGHTS -> {} // no telemetry needed
}
openLinkInNormalTab(item.url)
}
is AboutItem.Libraries -> {
- requireComponents.analytics.metrics.track(Event.LibrariesThatWeUseTapped)
openLibrariesPage()
}
is AboutItem.Crashes -> {
diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/AboutLibrariesActivity.kt b/app/src/main/java/org/mozilla/fenix/settings/about/AboutLibrariesFragment.kt
similarity index 80%
rename from app/src/main/java/org/mozilla/fenix/settings/about/AboutLibrariesActivity.kt
rename to app/src/main/java/org/mozilla/fenix/settings/about/AboutLibrariesFragment.kt
index bf69fb2f3..bf2e2b6a8 100644
--- a/app/src/main/java/org/mozilla/fenix/settings/about/AboutLibrariesActivity.kt
+++ b/app/src/main/java/org/mozilla/fenix/settings/about/AboutLibrariesFragment.kt
@@ -7,12 +7,15 @@ package org.mozilla.fenix.settings.about
import android.graphics.Typeface
import android.os.Bundle
import android.text.util.Linkify
+import android.view.View
import android.widget.ArrayAdapter
import android.widget.ListView
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
-import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import kotlinx.android.synthetic.main.fragment_about_libraries.view.*
import org.mozilla.fenix.R
+import org.mozilla.fenix.ext.showToolbar
import java.nio.charset.Charset
import java.util.Locale
@@ -27,33 +30,24 @@ import java.util.Locale
* such as AboutLibraries (https://github.com/mikepenz/AboutLibraries)
* but we considered the risk of introducing such third-party dependency
* to Fenix too high. Therefore, we use Google's gradle plugin to
- * extract the dependencies and their licenses, and this activity
+ * extract the dependencies and their licenses, and this fragment
* to show the extracted licenses to the end-user.
*/
-class AboutLibrariesActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
+class AboutLibrariesFragment : Fragment(R.layout.fragment_about_libraries) {
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val appName = getString(R.string.app_name)
- title = getString(R.string.open_source_licenses_title, appName)
- setContentView(R.layout.about_libraries_activity)
-
- setSupportActionBar(findViewById(R.id.toolbar))
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- supportActionBar?.setDisplayShowHomeEnabled(true)
-
- setupLibrariesListView()
- }
+ showToolbar(getString(R.string.open_source_licenses_title, appName))
- override fun onSupportNavigateUp(): Boolean {
- onBackPressed()
- return true
+ setupLibrariesListView(view.about_libraries_listview)
}
- private fun setupLibrariesListView() {
+ private fun setupLibrariesListView(listView: ListView) {
val libraries = parseLibraries()
- val listView = findViewById(R.id.about_libraries_listview)
- listView.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, libraries)
+ listView.adapter = ArrayAdapter(
+ listView.context,
+ android.R.layout.simple_list_item_1,
+ libraries
+ )
listView.setOnItemClickListener { _, _, position, _ ->
showLicenseDialog(libraries[position])
}
@@ -95,7 +89,7 @@ class AboutLibrariesActivity : AppCompatActivity() {
}
private fun showLicenseDialog(libraryItem: LibraryItem) {
- val dialog = AlertDialog.Builder(this)
+ val dialog = AlertDialog.Builder(requireContext())
.setTitle(libraryItem.name)
.setMessage(libraryItem.license)
.create()
diff --git a/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt
index 0eab4909c..732f89671 100644
--- a/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt
@@ -36,7 +36,6 @@ import mozilla.components.service.fxa.sync.SyncStatusObserver
import mozilla.components.service.fxa.sync.getLastSynced
import mozilla.components.support.ktx.android.content.getColorFromAttr
import mozilla.components.support.ktx.android.util.dpToPx
-import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.StoreProvider
@@ -271,9 +270,8 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
isChecked = syncEnginesStatus.getOrElse(SyncEngine.Passwords) { true }
}
requirePreference(R.string.pref_key_sync_tabs).apply {
- isVisible = FeatureFlags.syncedTabs
isEnabled = syncEnginesStatus.containsKey(SyncEngine.Tabs)
- isChecked = syncEnginesStatus.getOrElse(SyncEngine.Tabs) { FeatureFlags.syncedTabs }
+ isChecked = syncEnginesStatus.getOrElse(SyncEngine.Tabs) { true }
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/settings/account/TurnOnSyncFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/account/TurnOnSyncFragment.kt
index ceedd2309..acfa74c2a 100644
--- a/app/src/main/java/org/mozilla/fenix/settings/account/TurnOnSyncFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/settings/account/TurnOnSyncFragment.kt
@@ -17,6 +17,7 @@ import kotlinx.android.synthetic.main.fragment_turn_on_sync.view.*
import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount
+import mozilla.components.support.ktx.android.content.hasCamera
import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.metrics.Event
@@ -26,15 +27,11 @@ import org.mozilla.fenix.ext.showToolbar
class TurnOnSyncFragment : Fragment(), AccountObserver {
private val args by navArgs()
+ private var shouldLoginJustWithEmail = false
+ private var pairWithEmailStarted = false
private val signInClickListener = View.OnClickListener {
- requireComponents.services.accountsAuthFeature.beginAuthentication(requireContext())
- requireComponents.analytics.metrics.track(Event.SyncAuthUseEmail)
- // TODO The sign-in web content populates session history,
- // so pressing "back" after signing in won't take us back into the settings screen, but rather up the
- // session history stack.
- // We could auto-close this tab once we get to the end of the authentication process?
- // Via an interceptor, perhaps.
+ navigateToPairWithEmail()
}
private val paringClickListener = View.OnClickListener {
@@ -46,6 +43,14 @@ class TurnOnSyncFragment : Fragment(), AccountObserver {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requireComponents.analytics.metrics.track(Event.SyncAuthOpened)
+
+ // App can be installed on devices with no camera modules. Like Android TV boxes.
+ // Let's skip presenting the option to sign in by scanning a qr code in this case
+ // and default to login with email and password.
+ shouldLoginJustWithEmail = !requireContext().hasCamera()
+ if (shouldLoginJustWithEmail) {
+ navigateToPairWithEmail()
+ }
}
override fun onDestroy() {
@@ -55,16 +60,28 @@ class TurnOnSyncFragment : Fragment(), AccountObserver {
override fun onResume() {
super.onResume()
- if (requireComponents.backgroundServices.accountManager.authenticatedAccount() != null) {
+ if (pairWithEmailStarted ||
+ requireComponents.backgroundServices.accountManager.authenticatedAccount() != null) {
+
findNavController().popBackStack()
return
}
- requireComponents.backgroundServices.accountManager.register(this, owner = this)
- showToolbar(getString(R.string.preferences_sync))
+ if (shouldLoginJustWithEmail) {
+ // Next time onResume is called, after returning from pairing with email this Fragment will be popped.
+ pairWithEmailStarted = true
+ } else {
+ requireComponents.backgroundServices.accountManager.register(this, owner = this)
+ showToolbar(getString(R.string.preferences_sync))
+ }
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ if (shouldLoginJustWithEmail) {
+ // Headless fragment. Don't need UI if we're taking the user to another screen.
+ return null
+ }
+
val view = inflater.inflate(R.layout.fragment_turn_on_sync, container, false)
view.signInScanButton.setOnClickListener(paringClickListener)
view.signInEmailButton.setOnClickListener(signInClickListener)
@@ -99,4 +116,14 @@ class TurnOnSyncFragment : Fragment(), AccountObserver {
.show()
}
}
+
+ private fun navigateToPairWithEmail() {
+ requireComponents.services.accountsAuthFeature.beginAuthentication(requireContext())
+ requireComponents.analytics.metrics.track(Event.SyncAuthUseEmail)
+ // TODO The sign-in web content populates session history,
+ // so pressing "back" after signing in won't take us back into the settings screen, but rather up the
+ // session history stack.
+ // We could auto-close this tab once we get to the end of the authentication process?
+ // Via an interceptor, perhaps.
+ }
}
diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceView.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceView.kt
index 81d742d39..49e313b57 100644
--- a/app/src/main/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceView.kt
+++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceView.kt
@@ -4,21 +4,16 @@
package org.mozilla.fenix.settings.logins
-import android.content.Context
import androidx.lifecycle.LifecycleOwner
import androidx.navigation.NavController
import androidx.preference.Preference
import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount
-import mozilla.components.feature.accounts.FirefoxAccountsAuthFeature
import mozilla.components.service.fxa.SyncEngine
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.fxa.manager.SyncEnginesStorage
-import mozilla.components.support.ktx.android.content.hasCamera
import org.mozilla.fenix.R
-import org.mozilla.fenix.components.metrics.Event
-import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragmentDirections
/**
@@ -28,9 +23,7 @@ class SyncLoginsPreferenceView(
private val syncLoginsPreference: Preference,
lifecycleOwner: LifecycleOwner,
accountManager: FxaAccountManager,
- private val navController: NavController,
- private val accountsAuthFeature: FirefoxAccountsAuthFeature,
- private val metrics: MetricController
+ private val navController: NavController
) {
init {
@@ -75,15 +68,7 @@ class SyncLoginsPreferenceView(
syncLoginsPreference.apply {
summary = context.getString(R.string.preferences_passwords_sync_logins_sign_in)
setOnPreferenceClickListener {
- // App can be installed on devices with no camera modules. Like Android TV boxes.
- // Let's skip presenting the option to sign in by scanning a qr code in this case
- // and default to login with email and password.
- if (context.hasCamera()) {
- navigateToTurnOnSyncFragment()
- } else {
- navigateToPairWithEmail(context)
- }
-
+ navigateToTurnOnSyncFragment()
true
}
}
@@ -117,9 +102,4 @@ class SyncLoginsPreferenceView(
val directions = SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToTurnOnSyncFragment()
navController.navigate(directions)
}
-
- private fun navigateToPairWithEmail(context: Context) {
- accountsAuthFeature.beginAuthentication(context)
- metrics.track(Event.SyncAuthUseEmail)
- }
}
diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/LoginDetailFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/LoginDetailFragment.kt
index acd6f1320..ed452e33b 100644
--- a/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/LoginDetailFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/LoginDetailFragment.kt
@@ -25,7 +25,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
import mozilla.components.lib.state.ext.consumeFrom
import org.mozilla.fenix.BrowserDirection
-import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar
@@ -169,11 +168,7 @@ class LoginDetailFragment : Fragment(R.layout.fragment_login_detail) {
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- if (FeatureFlags.loginsEdit) {
- inflater.inflate(R.menu.login_options_menu, menu)
- } else {
- inflater.inflate(R.menu.login_delete, menu)
- }
+ inflater.inflate(R.menu.login_options_menu, menu)
this.menu = menu
}
diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/SavedLoginsAuthFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/SavedLoginsAuthFragment.kt
index a7493f99b..4246beccd 100644
--- a/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/SavedLoginsAuthFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/SavedLoginsAuthFragment.kt
@@ -145,9 +145,7 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat() {
requirePreference(R.string.pref_key_password_sync_logins),
lifecycleOwner = viewLifecycleOwner,
accountManager = requireComponents.backgroundServices.accountManager,
- navController = findNavController(),
- accountsAuthFeature = requireComponents.services.accountsAuthFeature,
- metrics = requireComponents.analytics.metrics
+ navController = findNavController()
)
togglePrefsEnabledWhileAuthenticating(enabled = true)
diff --git a/app/src/main/java/org/mozilla/fenix/shortcut/CreateShortcutFragment.kt b/app/src/main/java/org/mozilla/fenix/shortcut/CreateShortcutFragment.kt
index 9918a677e..836bfce84 100644
--- a/app/src/main/java/org/mozilla/fenix/shortcut/CreateShortcutFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/shortcut/CreateShortcutFragment.kt
@@ -41,7 +41,7 @@ class CreateShortcutFragment : DialogFragment() {
cancel_button.setOnClickListener { dismiss() }
add_button.setOnClickListener {
- val text = shortcut_text.text.toString()
+ val text = shortcut_text.text.toString().trim()
requireActivity().lifecycleScope.launch {
requireComponents.useCases.webAppUseCases.addToHomescreen(text)
}
@@ -57,8 +57,9 @@ class CreateShortcutFragment : DialogFragment() {
}
private fun updateAddButtonEnabledState() {
- add_button.isEnabled = shortcut_text.text.isNotEmpty()
- add_button.alpha = if (shortcut_text.text.isNotEmpty()) ENABLED_ALPHA else DISABLED_ALPHA
+ val text = shortcut_text.text
+ add_button.isEnabled = text.isNotBlank()
+ add_button.alpha = if (text.isNotBlank()) ENABLED_ALPHA else DISABLED_ALPHA
}
companion object {
diff --git a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsAdapter.kt
index e51419df4..6e30a4711 100644
--- a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsAdapter.kt
+++ b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsAdapter.kt
@@ -10,14 +10,18 @@ import androidx.navigation.NavController
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import mozilla.components.browser.storage.sync.SyncedDeviceTabs
+import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import org.mozilla.fenix.sync.SyncedTabsViewHolder.DeviceViewHolder
import org.mozilla.fenix.sync.SyncedTabsViewHolder.ErrorViewHolder
+import org.mozilla.fenix.sync.SyncedTabsViewHolder.NoTabsViewHolder
import org.mozilla.fenix.sync.SyncedTabsViewHolder.TabViewHolder
+import org.mozilla.fenix.sync.SyncedTabsViewHolder.TitleViewHolder
+import org.mozilla.fenix.sync.ext.toAdapterList
import mozilla.components.browser.storage.sync.Tab as SyncTab
import mozilla.components.concept.sync.Device as SyncDevice
class SyncedTabsAdapter(
- private val listener: (SyncTab) -> Unit
+ private val newListener: SyncedTabsView.Listener
) : ListAdapter(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SyncedTabsViewHolder {
@@ -27,30 +31,26 @@ class SyncedTabsAdapter(
DeviceViewHolder.LAYOUT_ID -> DeviceViewHolder(itemView)
TabViewHolder.LAYOUT_ID -> TabViewHolder(itemView)
ErrorViewHolder.LAYOUT_ID -> ErrorViewHolder(itemView)
+ TitleViewHolder.LAYOUT_ID -> TitleViewHolder(itemView)
+ NoTabsViewHolder.LAYOUT_ID -> NoTabsViewHolder(itemView)
else -> throw IllegalStateException()
}
}
override fun onBindViewHolder(holder: SyncedTabsViewHolder, position: Int) {
- holder.bind(getItem(position), listener)
+ holder.bind(getItem(position), newListener)
}
override fun getItemViewType(position: Int) = when (getItem(position)) {
is AdapterItem.Device -> DeviceViewHolder.LAYOUT_ID
is AdapterItem.Tab -> TabViewHolder.LAYOUT_ID
is AdapterItem.Error -> ErrorViewHolder.LAYOUT_ID
+ is AdapterItem.Title -> TitleViewHolder.LAYOUT_ID
+ is AdapterItem.NoTabs -> NoTabsViewHolder.LAYOUT_ID
}
fun updateData(syncedTabs: List) {
- val allDeviceTabs = mutableListOf()
-
- syncedTabs.forEach { (device, tabs) ->
- if (tabs.isNotEmpty()) {
- allDeviceTabs.add(AdapterItem.Device(device))
- tabs.mapTo(allDeviceTabs) { AdapterItem.Tab(it) }
- }
- }
-
+ val allDeviceTabs = syncedTabs.toAdapterList()
submitList(allDeviceTabs)
}
@@ -59,7 +59,11 @@ class SyncedTabsAdapter(
when (oldItem) {
is AdapterItem.Device ->
newItem is AdapterItem.Device && oldItem.device.id == newItem.device.id
- is AdapterItem.Tab, is AdapterItem.Error ->
+ is AdapterItem.NoTabs ->
+ newItem is AdapterItem.NoTabs && oldItem.device.id == newItem.device.id
+ is AdapterItem.Tab,
+ is AdapterItem.Error,
+ is AdapterItem.Title ->
oldItem == newItem
}
@@ -68,9 +72,35 @@ class SyncedTabsAdapter(
oldItem == newItem
}
+ /**
+ * The various types of adapter items that can be found in a [SyncedTabsAdapter].
+ */
sealed class AdapterItem {
+
+ /**
+ * A title header of the Synced Tabs UI that has a refresh button in it. This may be seen
+ * only in some views depending on where the Synced Tabs UI is displayed.
+ */
+ object Title : AdapterItem()
+
+ /**
+ * A device header for displaying a synced device.
+ */
data class Device(val device: SyncDevice) : AdapterItem()
+
+ /**
+ * A tab that was synced.
+ */
data class Tab(val tab: SyncTab) : AdapterItem()
+
+ /**
+ * A placeholder for a device that has no tabs synced.
+ */
+ data class NoTabs(val device: SyncDevice) : AdapterItem()
+
+ /**
+ * A message displayed if an error was encountered.
+ */
data class Error(
val descriptionResId: Int,
val navController: NavController? = null
diff --git a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsFragment.kt b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsFragment.kt
index 4059d5159..cd67fe06b 100644
--- a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsFragment.kt
@@ -13,6 +13,7 @@ import mozilla.components.browser.storage.sync.Tab
import mozilla.components.feature.syncedtabs.SyncedTabsFeature
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import org.mozilla.fenix.BrowserDirection
+import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
@@ -22,6 +23,11 @@ import org.mozilla.fenix.library.LibraryPageFragment
class SyncedTabsFragment : LibraryPageFragment() {
private val syncedTabsFeature = ViewBoundFeatureWrapper()
+ init {
+ // Sanity-check: Remove this class when the feature flag is always enabled.
+ FeatureFlags.syncedTabsInTabsTray
+ }
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
diff --git a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsLayout.kt b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsLayout.kt
index 8308692ba..2d3b5c0a4 100644
--- a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsLayout.kt
+++ b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsLayout.kt
@@ -7,7 +7,6 @@ package org.mozilla.fenix.sync
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
-import androidx.annotation.StringRes
import androidx.fragment.app.findFragment
import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController
@@ -18,8 +17,12 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import mozilla.components.browser.storage.sync.SyncedDeviceTabs
+import mozilla.components.browser.storage.sync.Tab
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
+import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
+import org.mozilla.fenix.sync.ext.toAdapterItem
+import org.mozilla.fenix.sync.ext.toStringRes
import java.lang.IllegalStateException
class SyncedTabsLayout @JvmOverloads constructor(
@@ -30,7 +33,7 @@ class SyncedTabsLayout @JvmOverloads constructor(
override var listener: SyncedTabsView.Listener? = null
- private val adapter = SyncedTabsAdapter { listener?.onTabClicked(it) }
+ private val adapter = SyncedTabsAdapter(ListenerDelegate { listener })
private val coroutineScope = CoroutineScope(Dispatchers.Main)
init {
@@ -40,6 +43,9 @@ class SyncedTabsLayout @JvmOverloads constructor(
synced_tabs_list.adapter = adapter
synced_tabs_pull_to_refresh.setOnRefreshListener { listener?.onRefresh() }
+
+ // Sanity-check: Remove this class when the feature flag is always enabled.
+ FeatureFlags.syncedTabsInTabsTray
}
override fun onError(error: SyncedTabsView.ErrorType) {
@@ -53,8 +59,8 @@ class SyncedTabsLayout @JvmOverloads constructor(
null
}
- val descriptionResId = stringResourceForError(error)
- val errorItem = getErrorItem(navController, error, descriptionResId)
+ val descriptionResId = error.toStringRes()
+ val errorItem = error.toAdapterItem(descriptionResId, navController)
val errorList: List = listOf(errorItem)
adapter.submitList(errorList)
@@ -96,27 +102,21 @@ class SyncedTabsLayout @JvmOverloads constructor(
SyncedTabsView.ErrorType.MULTIPLE_DEVICES_UNAVAILABLE,
SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> true
}
+ }
+}
- internal fun stringResourceForError(error: SyncedTabsView.ErrorType) = when (error) {
- SyncedTabsView.ErrorType.MULTIPLE_DEVICES_UNAVAILABLE -> R.string.synced_tabs_connect_another_device
- SyncedTabsView.ErrorType.SYNC_ENGINE_UNAVAILABLE -> R.string.synced_tabs_enable_tab_syncing
- SyncedTabsView.ErrorType.SYNC_UNAVAILABLE -> R.string.synced_tabs_sign_in_message
- SyncedTabsView.ErrorType.SYNC_NEEDS_REAUTHENTICATION -> R.string.synced_tabs_reauth
- SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> R.string.synced_tabs_no_tabs
- }
+/**
+ * We have to do this weird daisy-chaining of callbacks because the listener is nullable and
+ * when we get a null reference, we never get a new binding to the non-null listener.
+ */
+class ListenerDelegate(
+ private val listener: (() -> SyncedTabsView.Listener?)
+) : SyncedTabsView.Listener {
+ override fun onRefresh() {
+ listener.invoke()?.onRefresh()
+ }
- internal fun getErrorItem(
- navController: NavController?,
- error: SyncedTabsView.ErrorType,
- @StringRes stringResId: Int
- ): SyncedTabsAdapter.AdapterItem = when (error) {
- SyncedTabsView.ErrorType.MULTIPLE_DEVICES_UNAVAILABLE,
- SyncedTabsView.ErrorType.SYNC_ENGINE_UNAVAILABLE,
- SyncedTabsView.ErrorType.SYNC_NEEDS_REAUTHENTICATION,
- SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> SyncedTabsAdapter.AdapterItem
- .Error(descriptionResId = stringResId)
- SyncedTabsView.ErrorType.SYNC_UNAVAILABLE -> SyncedTabsAdapter.AdapterItem
- .Error(descriptionResId = stringResId, navController = navController)
- }
+ override fun onTabClicked(tab: Tab) {
+ listener.invoke()?.onTabClicked(tab)
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsViewHolder.kt b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsViewHolder.kt
index 95abada88..4549f570e 100644
--- a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsViewHolder.kt
+++ b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsViewHolder.kt
@@ -7,29 +7,36 @@ package org.mozilla.fenix.sync
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
+import android.view.animation.Animation
+import android.view.animation.AnimationUtils
import android.widget.LinearLayout
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.sync_tabs_error_row.view.*
import kotlinx.android.synthetic.main.sync_tabs_list_item.view.*
import kotlinx.android.synthetic.main.view_synced_tabs_group.view.*
-import mozilla.components.browser.storage.sync.Tab
+import kotlinx.android.synthetic.main.view_synced_tabs_title.view.*
import mozilla.components.concept.sync.DeviceType
+import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.support.ktx.android.util.dpToPx
import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.R
import org.mozilla.fenix.sync.SyncedTabsAdapter.AdapterItem
+/**
+ * The various view-holders that can be found in a [SyncedTabsAdapter]. For more
+ * descriptive information on the different types, see the docs for [AdapterItem].
+ */
sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
- abstract fun bind(item: T, interactor: (Tab) -> Unit)
+ abstract fun bind(item: T, interactor: SyncedTabsView.Listener)
class TabViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) {
- override fun bind(item: T, interactor: (Tab) -> Unit) {
+ override fun bind(item: T, interactor: SyncedTabsView.Listener) {
bindTab(item as AdapterItem.Tab)
itemView.setOnClickListener {
- interactor(item.tab)
+ interactor.onTabClicked(item.tab)
}
}
@@ -46,7 +53,7 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item
class ErrorViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) {
- override fun bind(item: T, interactor: (Tab) -> Unit) {
+ override fun bind(item: T, interactor: SyncedTabsView.Listener) {
val errorItem = item as AdapterItem.Error
setErrorMargins()
@@ -69,7 +76,7 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item
class DeviceViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) {
- override fun bind(item: T, interactor: (Tab) -> Unit) {
+ override fun bind(item: T, interactor: SyncedTabsView.Listener) {
bindHeader(item as AdapterItem.Device)
}
@@ -93,6 +100,36 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item
}
}
+ class NoTabsViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) {
+ override fun bind(item: T, interactor: SyncedTabsView.Listener) = Unit
+
+ companion object {
+ const val LAYOUT_ID = R.layout.view_synced_tabs_no_item
+ }
+ }
+
+ class TitleViewHolder(itemView: View) : SyncedTabsViewHolder(itemView) {
+
+ override fun bind(item: T, interactor: SyncedTabsView.Listener) {
+ itemView.refresh_icon.setOnClickListener { v ->
+ val rotation = AnimationUtils.loadAnimation(
+ itemView.context,
+ R.anim.full_rotation
+ ).apply {
+ repeatCount = Animation.ABSOLUTE
+ }
+
+ v.startAnimation(rotation)
+
+ interactor.onRefresh()
+ }
+ }
+
+ companion object {
+ const val LAYOUT_ID = R.layout.view_synced_tabs_title
+ }
+ }
+
internal fun setErrorMargins() {
val lp = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
diff --git a/app/src/main/java/org/mozilla/fenix/sync/ext/ErrorType.kt b/app/src/main/java/org/mozilla/fenix/sync/ext/ErrorType.kt
new file mode 100644
index 000000000..1a24ba455
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/sync/ext/ErrorType.kt
@@ -0,0 +1,38 @@
+/* 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.sync.ext
+
+import androidx.annotation.StringRes
+import androidx.navigation.NavController
+import mozilla.components.feature.syncedtabs.view.SyncedTabsView.ErrorType
+import org.mozilla.fenix.R
+import org.mozilla.fenix.sync.SyncedTabsAdapter
+
+/**
+ * Converts the error type to the appropriate matching string resource for displaying to the user.
+ */
+fun ErrorType.toStringRes() = when (this) {
+ ErrorType.MULTIPLE_DEVICES_UNAVAILABLE -> R.string.synced_tabs_connect_another_device
+ ErrorType.SYNC_ENGINE_UNAVAILABLE -> R.string.synced_tabs_enable_tab_syncing
+ ErrorType.SYNC_UNAVAILABLE -> R.string.synced_tabs_sign_in_message
+ ErrorType.SYNC_NEEDS_REAUTHENTICATION -> R.string.synced_tabs_reauth
+ ErrorType.NO_TABS_AVAILABLE -> R.string.synced_tabs_no_tabs
+}
+
+/**
+ * Converts an error type to an [SyncedTabsAdapter.AdapterItem.Error].
+ */
+fun ErrorType.toAdapterItem(
+ @StringRes stringResId: Int,
+ navController: NavController? = null
+) = when (this) {
+ ErrorType.MULTIPLE_DEVICES_UNAVAILABLE,
+ ErrorType.SYNC_ENGINE_UNAVAILABLE,
+ ErrorType.SYNC_NEEDS_REAUTHENTICATION,
+ ErrorType.NO_TABS_AVAILABLE -> SyncedTabsAdapter.AdapterItem
+ .Error(descriptionResId = stringResId)
+ ErrorType.SYNC_UNAVAILABLE -> SyncedTabsAdapter.AdapterItem
+ .Error(descriptionResId = stringResId, navController = navController)
+}
diff --git a/app/src/main/java/org/mozilla/fenix/sync/ext/SyncedTabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/sync/ext/SyncedTabsAdapter.kt
new file mode 100644
index 000000000..18c2c1be9
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/sync/ext/SyncedTabsAdapter.kt
@@ -0,0 +1,22 @@
+/* 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.sync.ext
+
+import mozilla.components.browser.storage.sync.SyncedDeviceTabs
+import org.mozilla.fenix.sync.SyncedTabsAdapter.AdapterItem
+
+/**
+ * Converts a list of [SyncedDeviceTabs] into a list of [AdapterItem].
+ */
+fun List.toAdapterList() = asSequence().flatMap { (device, tabs) ->
+
+ val deviceTabs = if (tabs.isEmpty()) {
+ sequenceOf(AdapterItem.NoTabs(device))
+ } else {
+ tabs.asSequence().map { AdapterItem.Tab(it) }
+ }
+
+ sequenceOf(AdapterItem.Device(device)) + deviceTabs
+}.toList()
diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/CollectionsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabtray/CollectionsAdapter.kt
index 67eeba43b..f80e48406 100644
--- a/app/src/main/java/org/mozilla/fenix/tabtray/CollectionsAdapter.kt
+++ b/app/src/main/java/org/mozilla/fenix/tabtray/CollectionsAdapter.kt
@@ -9,6 +9,7 @@ import android.view.ViewGroup
import android.widget.CheckedTextView
import androidx.annotation.VisibleForTesting
import androidx.core.content.ContextCompat
+import androidx.core.view.updatePaddingRelative
import androidx.recyclerview.widget.RecyclerView
import mozilla.components.support.ktx.android.util.dpToPx
import org.mozilla.fenix.R
@@ -36,7 +37,7 @@ internal class CollectionsAdapter(
override fun onBindViewHolder(holder: CollectionItemViewHolder, position: Int) {
if (position == 0) {
val displayMetrics = holder.textView.context.resources.displayMetrics
- holder.textView.setPadding(NEW_COLLECTION_PADDING_START.dpToPx(displayMetrics), 0, 0, 0)
+ holder.textView.updatePaddingRelative(start = NEW_COLLECTION_PADDING_START.dpToPx(displayMetrics))
holder.textView.compoundDrawablePadding =
NEW_COLLECTION_DRAWABLE_PADDING.dpToPx(displayMetrics)
holder.textView.setCompoundDrawablesWithIntrinsicBounds(
diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/SyncedTabsController.kt b/app/src/main/java/org/mozilla/fenix/tabtray/SyncedTabsController.kt
new file mode 100644
index 000000000..8fbb87c63
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/tabtray/SyncedTabsController.kt
@@ -0,0 +1,85 @@
+/* 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.tabtray
+
+import android.view.View
+import androidx.fragment.app.FragmentManager.findFragment
+import androidx.lifecycle.LifecycleOwner
+import androidx.navigation.NavController
+import androidx.navigation.fragment.findNavController
+import androidx.recyclerview.widget.ConcatAdapter
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.drop
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+import mozilla.components.browser.storage.sync.SyncedDeviceTabs
+import mozilla.components.feature.syncedtabs.view.SyncedTabsView
+import mozilla.components.lib.state.ext.flowScoped
+import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
+import org.mozilla.fenix.sync.ListenerDelegate
+import org.mozilla.fenix.sync.SyncedTabsAdapter
+import org.mozilla.fenix.sync.ext.toAdapterList
+import org.mozilla.fenix.sync.ext.toAdapterItem
+import org.mozilla.fenix.sync.ext.toStringRes
+import kotlin.coroutines.CoroutineContext
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class SyncedTabsController(
+ lifecycleOwner: LifecycleOwner,
+ private val view: View,
+ store: TabTrayDialogFragmentStore,
+ private val concatAdapter: ConcatAdapter,
+ coroutineContext: CoroutineContext = Dispatchers.Main
+) : SyncedTabsView {
+ override var listener: SyncedTabsView.Listener? = null
+
+ val adapter = SyncedTabsAdapter(ListenerDelegate { listener })
+
+ private val scope: CoroutineScope = CoroutineScope(coroutineContext)
+
+ init {
+ store.flowScoped(lifecycleOwner) { flow ->
+ flow.map { it.mode }
+ .ifChanged()
+ .drop(1)
+ .collect { mode ->
+ when (mode) {
+ is TabTrayDialogFragmentState.Mode.Normal -> {
+ concatAdapter.addAdapter(0, adapter)
+ }
+ is TabTrayDialogFragmentState.Mode.MultiSelect -> {
+ concatAdapter.removeAdapter(adapter)
+ }
+ }
+ }
+ }
+ }
+
+ override fun displaySyncedTabs(syncedTabs: List) {
+ scope.launch {
+ val tabsList = listOf(SyncedTabsAdapter.AdapterItem.Title) + syncedTabs.toAdapterList()
+ // Reverse layout for TabTrayView which does things backwards.
+ adapter.submitList(tabsList.reversed())
+ }
+ }
+
+ override fun onError(error: SyncedTabsView.ErrorType) {
+ scope.launch {
+ val navController: NavController? = try {
+ findFragment(view).findNavController()
+ } catch (exception: IllegalStateException) {
+ null
+ }
+
+ val descriptionResId = error.toStringRes()
+ val errorItem = error.toAdapterItem(descriptionResId, navController)
+
+ adapter.submitList(listOf(errorItem))
+ }
+ }
+}
diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt
index ad71ced1c..7a3a03eee 100644
--- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt
+++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt
@@ -9,10 +9,12 @@ import androidx.navigation.NavController
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
+import mozilla.components.browser.storage.sync.Tab as SyncTab
import mozilla.components.concept.engine.profiler.Profiler
import mozilla.components.concept.engine.prompt.ShareData
import mozilla.components.concept.tabstray.Tab
import mozilla.components.feature.tabs.TabsUseCases
+import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
@@ -30,6 +32,7 @@ interface TabTrayController {
fun onNewTabTapped(private: Boolean)
fun onTabTrayDismissed()
fun onShareTabsClicked(private: Boolean)
+ fun onSyncedTabClicked(syncTab: SyncTab)
fun onSaveToCollectionClicked(selectedTabs: Set)
fun onCloseAllTabsClicked(private: Boolean)
fun handleBackPressed(): Boolean
@@ -59,6 +62,7 @@ interface TabTrayController {
*/
@Suppress("TooManyFunctions")
class DefaultTabTrayController(
+ private val activity: HomeActivity,
private val profiler: Profiler?,
private val sessionManager: SessionManager,
private val browsingModeManager: BrowsingModeManager,
@@ -117,6 +121,14 @@ class DefaultTabTrayController(
navController.navigate(directions)
}
+ override fun onSyncedTabClicked(syncTab: SyncTab) {
+ activity.openToBrowserAndLoad(
+ searchTermOrURL = syncTab.active().url,
+ newTab = true,
+ from = BrowserDirection.FromTabTray
+ )
+ }
+
@OptIn(ExperimentalCoroutinesApi::class)
override fun onCloseAllTabsClicked(private: Boolean) {
val sessionsToClose = if (private) {
diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt
index cfe8b0f1c..3b64217f4 100644
--- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt
+++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt
@@ -177,6 +177,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
adapter,
interactor = TabTrayFragmentInteractor(
DefaultTabTrayController(
+ activity = activity,
profiler = activity.components.core.engine.profiler,
sessionManager = activity.components.core.sessionManager,
browsingModeManager = activity.browsingModeManager,
@@ -191,10 +192,11 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
showAddNewCollectionDialog = ::showAddNewCollectionDialog
)
),
+ store = tabTrayDialogStore,
isPrivate = isPrivate,
startingInLandscape = requireContext().resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE,
- lifecycleScope = viewLifecycleOwner.lifecycleScope
+ lifecycleOwner = viewLifecycleOwner
) { private ->
val filter: (TabSessionState) -> Boolean = { state -> private == state.content.private }
diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayFragmentInteractor.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayFragmentInteractor.kt
index 33bef403a..b6a65dd77 100644
--- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayFragmentInteractor.kt
+++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayFragmentInteractor.kt
@@ -5,6 +5,7 @@
package org.mozilla.fenix.tabtray
import mozilla.components.concept.tabstray.Tab
+import mozilla.components.browser.storage.sync.Tab as SyncTab
@Suppress("TooManyFunctions")
interface TabTrayInteractor {
@@ -33,6 +34,11 @@ interface TabTrayInteractor {
*/
fun onCloseAllTabsClicked(private: Boolean)
+ /**
+ * Called when the user clicks on a synced tab entry.
+ */
+ fun onSyncedTabClicked(syncTab: SyncTab)
+
/**
* Called when the physical back button is clicked.
*/
@@ -89,6 +95,10 @@ class TabTrayFragmentInteractor(private val controller: TabTrayController) : Tab
controller.onCloseAllTabsClicked(private)
}
+ override fun onSyncedTabClicked(syncTab: SyncTab) {
+ controller.onSyncedTabClicked(syncTab)
+ }
+
override fun onBackPressed(): Boolean {
return controller.handleBackPressed()
}
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 9b78836ff..4963c044e 100644
--- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt
+++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt
@@ -16,7 +16,9 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
-import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.core.view.updatePadding
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
@@ -36,6 +38,8 @@ import mozilla.components.browser.state.selector.normalTabs
import mozilla.components.browser.state.selector.privateTabs
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.tabstray.TabViewHolder
+import mozilla.components.feature.syncedtabs.SyncedTabsFeature
+import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import mozilla.components.support.ktx.android.util.dpToPx
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
@@ -47,6 +51,7 @@ import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.tabtray.SaveToCollectionsButtonAdapter.MultiselectModeChange
import org.mozilla.fenix.tabtray.TabTrayDialogFragmentState.Mode
import java.text.NumberFormat
+import mozilla.components.browser.storage.sync.Tab as SyncTab
/**
* View that contains and configures the BrowserAwesomeBar
@@ -56,11 +61,13 @@ class TabTrayView(
private val container: ViewGroup,
private val tabsAdapter: FenixTabsAdapter,
private val interactor: TabTrayInteractor,
+ store: TabTrayDialogFragmentStore,
isPrivate: Boolean,
startingInLandscape: Boolean,
- lifecycleScope: LifecycleCoroutineScope,
+ lifecycleOwner: LifecycleOwner,
private val filterTabs: (Boolean) -> Unit
) : LayoutContainer, TabLayout.OnTabSelectedListener {
+ val lifecycleScope = lifecycleOwner.lifecycleScope
val fabView = LayoutInflater.from(container.context)
.inflate(R.layout.component_tabstray_fab, container, true)
@@ -73,19 +80,25 @@ class TabTrayView(
private val behavior = BottomSheetBehavior.from(view.tab_wrapper)
+ private val concatAdapter = ConcatAdapter(tabsAdapter)
private val tabTrayItemMenu: TabTrayItemMenu
private var menu: BrowserMenu? = null
private var tabsTouchHelper: TabsTouchHelper
private val collectionsButtonAdapter = SaveToCollectionsButtonAdapter(interactor, isPrivate)
+ private val syncedTabsController = SyncedTabsController(lifecycleOwner, view, store, concatAdapter)
+ private val syncedTabsFeature = ViewBoundFeatureWrapper()
+
private var hasLoaded = false
override val containerView: View?
get() = container
+ private val components = container.context.components
+
init {
- container.context.components.analytics.metrics.track(Event.TabsTrayOpened)
+ components.analytics.metrics.track(Event.TabsTrayOpened)
toggleFabText(isPrivate)
@@ -102,7 +115,7 @@ class TabTrayView(
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
- container.context.components.analytics.metrics.track(Event.TabsTrayClosed)
+ components.analytics.metrics.track(Event.TabsTrayClosed)
interactor.onTabTrayDismissed()
}
}
@@ -135,7 +148,20 @@ class TabTrayView(
setTopOffset(startingInLandscape)
- val concatAdapter = ConcatAdapter(tabsAdapter)
+ if (view.context.settings().syncedTabsInTabsTray) {
+ syncedTabsFeature.set(
+ feature = SyncedTabsFeature(
+ context = container.context,
+ storage = components.backgroundServices.syncedTabsStorage,
+ accountManager = components.backgroundServices.accountManager,
+ view = syncedTabsController,
+ lifecycleOwner = lifecycleOwner,
+ onTabClicked = ::handleTabClicked
+ ),
+ owner = lifecycleOwner,
+ view = view
+ )
+ }
view.tabsTray.apply {
layoutManager = LinearLayoutManager(container.context).apply {
@@ -156,6 +182,9 @@ class TabTrayView(
// Put the 'Add to collections' button after the tabs have loaded.
concatAdapter.addAdapter(0, collectionsButtonAdapter)
+ // Put the Synced Tabs adapter at the end.
+ concatAdapter.addAdapter(0, syncedTabsController.adapter)
+
if (hasAccessibilityEnabled) {
tabsAdapter.notifyDataSetChanged()
}
@@ -193,7 +222,7 @@ class TabTrayView(
}
view.tab_tray_overflow.setOnClickListener {
- container.context.components.analytics.metrics.track(Event.TabsTrayMenuOpened)
+ components.analytics.metrics.track(Event.TabsTrayMenuOpened)
menu = tabTrayItemMenu.menuBuilder.build(container.context)
menu?.show(it)
?.also { pu ->
@@ -209,6 +238,10 @@ class TabTrayView(
adjustNewTabButtonsForNormalMode()
}
+ private fun handleTabClicked(tab: SyncTab) {
+ interactor.onSyncedTabClicked(tab)
+ }
+
private fun adjustNewTabButtonsForNormalMode() {
view.tab_tray_new_tab.apply {
isVisible = hasAccessibilityEnabled
@@ -234,7 +267,7 @@ class TabTrayView(
Event.NewTabTapped
}
- container.context.components.analytics.metrics.track(eventToSend)
+ components.analytics.metrics.track(eventToSend)
}
fun expand() {
@@ -261,17 +294,14 @@ class TabTrayView(
scrollToTab(view.context.components.core.store.state.selectedTabId)
if (isPrivateModeSelected) {
- container.context.components.analytics.metrics.track(Event.TabsTrayPrivateModeTapped)
+ components.analytics.metrics.track(Event.TabsTrayPrivateModeTapped)
} else {
- container.context.components.analytics.metrics.track(Event.TabsTrayNormalModeTapped)
+ components.analytics.metrics.track(Event.TabsTrayNormalModeTapped)
}
}
- override fun onTabReselected(tab: TabLayout.Tab?) { /*noop*/
- }
-
- override fun onTabUnselected(tab: TabLayout.Tab?) { /*noop*/
- }
+ override fun onTabReselected(tab: TabLayout.Tab?) = Unit
+ override fun onTabUnselected(tab: TabLayout.Tab?) = Unit
var mode: Mode = Mode.Normal
private set
@@ -464,7 +494,7 @@ class TabTrayView(
private fun updateTabCounter(count: Int): String {
if (count > MAX_VISIBLE_TABS) {
- counter_text.setPadding(0, 0, 0, INFINITE_CHAR_PADDING_BOTTOM)
+ counter_text.updatePadding(bottom = INFINITE_CHAR_PADDING_BOTTOM)
return SO_MANY_TABS_OPEN
}
return NumberFormat.getInstance().format(count.toLong())
@@ -513,7 +543,9 @@ class TabTrayView(
// We offset the tab index by the number of items in the other adapters.
// We add the offset, because the layoutManager is initialized with `reverseLayout`.
- val recyclerViewIndex = selectedBrowserTabIndex + collectionsButtonAdapter.itemCount
+ val recyclerViewIndex = selectedBrowserTabIndex +
+ collectionsButtonAdapter.itemCount +
+ syncedTabsController.adapter.itemCount
layoutManager?.scrollToPosition(recyclerViewIndex)
}
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 8ed15bf03..285e4d454 100644
--- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt
+++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionOverlay.kt
@@ -96,6 +96,8 @@ class TrackingProtectionOverlay(
trackingOnboardingDialog.apply {
setContentView(layout)
setCancelable(false)
+ // removing title or setting it as an empty string does not prevent a11y services from assigning one
+ setTitle(" ")
}
trackingOnboardingDialog.window?.let {
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 1297eb5bb..e8a4cb7c1 100644
--- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
+++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
@@ -54,6 +54,7 @@ class Settings(private val appContext: Context) : PreferencesHolder {
const val showLoginsSecureWarningMaxCount = 1
const val trackingProtectionOnboardingMaximumCount = 1
const val pwaVisitsToShowPromptMaxCount = 3
+ const val topSitesMaxCount = 16
const val FENIX_PREFERENCES = "fenix_preferences"
private const val showSearchWidgetCFRMaxCount = 3
@@ -62,6 +63,7 @@ class Settings(private val appContext: Context) : PreferencesHolder {
private const val ALLOWED_INT = 2
private const val CFR_COUNT_CONDITION_FOCUS_INSTALLED = 1
private const val CFR_COUNT_CONDITION_FOCUS_NOT_INSTALLED = 3
+ private const val MIN_DAYS_SINCE_FEEDBACK_PROMPT = 120
private fun Action.toInt() = when (this) {
Action.BLOCKED -> BLOCKED_INT
@@ -97,10 +99,20 @@ class Settings(private val appContext: Context) : PreferencesHolder {
override val preferences: SharedPreferences =
appContext.getSharedPreferences(FENIX_PREFERENCES, MODE_PRIVATE)
- var useNewSearchExperience by featureFlagPreference(
- appContext.getPreferenceKey(R.string.pref_key_use_new_search_experience),
+ var showTopFrecentSites by featureFlagPreference(
+ appContext.getPreferenceKey(R.string.pref_key_enable_top_frecent_sites),
default = false,
- featureFlag = FeatureFlags.newSearchExperience
+ featureFlag = FeatureFlags.topFrecentSite
+ )
+
+ var numberOfAppLaunches by intPreference(
+ appContext.getPreferenceKey(R.string.pref_key_times_app_opened),
+ default = 0
+ )
+
+ var lastReviewPromptTimeInMillis by longPreference(
+ appContext.getPreferenceKey(R.string.pref_key_last_review_prompt_shown_time),
+ default = 0L
)
var waitToShowPageUntilFirstPaint by featureFlagPreference(
@@ -109,6 +121,12 @@ class Settings(private val appContext: Context) : PreferencesHolder {
featureFlag = FeatureFlags.waitUntilPaintToDraw
)
+ var syncedTabsInTabsTray by featureFlagPreference(
+ appContext.getPreferenceKey(R.string.pref_key_synced_tabs_tabs_tray),
+ default = false,
+ featureFlag = FeatureFlags.syncedTabsInTabsTray
+ )
+
var forceEnableZoom by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_accessibility_force_enable_zoom),
default = false
@@ -243,6 +261,11 @@ class Settings(private val appContext: Context) : PreferencesHolder {
default = false
)
+ var showCollectionsPlaceholderOnHome by booleanPreference(
+ appContext.getPreferenceKey(R.string.pref_key_show_collections_placeholder_home),
+ default = true
+ )
+
val isCrashReportingEnabled: Boolean
get() = isCrashReportEnabledInBuild &&
preferences.getBoolean(
@@ -279,6 +302,7 @@ class Settings(private val appContext: Context) : PreferencesHolder {
!trackingProtectionOnboardingShownThisSession)
var showSecretDebugMenuThisSession = false
+ var showNotificationsSetting = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
val shouldShowSecurityPinWarningSync: Boolean
get() = loginsSecureWarningSyncCount < showLoginsSecureWarningSyncMaxCount
@@ -822,6 +846,11 @@ class Settings(private val appContext: Context) : PreferencesHolder {
default = 0
)
+ val topSitesMaxLimit by intPreference(
+ appContext.getPreferenceKey(R.string.pref_key_top_sites_max_limit),
+ default = topSitesMaxCount
+ )
+
fun setOpenTabsCount(count: Int) {
preferences.edit().putInt(
appContext.getPreferenceKey(R.string.pref_key_open_tabs_count),
diff --git a/app/src/main/java/org/mozilla/fenix/utils/Undo.kt b/app/src/main/java/org/mozilla/fenix/utils/Undo.kt
index 6e8997770..543e2b257 100644
--- a/app/src/main/java/org/mozilla/fenix/utils/Undo.kt
+++ b/app/src/main/java/org/mozilla/fenix/utils/Undo.kt
@@ -6,6 +6,7 @@ package org.mozilla.fenix.utils
import android.view.View
import androidx.appcompat.widget.ContentFrameLayout
+import androidx.core.view.updatePadding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@@ -70,11 +71,8 @@ fun CoroutineScope.allowUndo(
val toolbarHeight = view.context.resources
.getDimensionPixelSize(R.dimen.browser_toolbar_height)
- snackbar.view.setPadding(
- 0,
- 0,
- 0,
- if (
+ snackbar.view.updatePadding(
+ bottom = if (
paddedForBottomToolbar &&
shouldUseBottomToolbar &&
// If the view passed in is a ContentFrameLayout, it does not matter
diff --git a/app/src/main/java/org/mozilla/fenix/widget/VoiceSearchActivity.kt b/app/src/main/java/org/mozilla/fenix/widget/VoiceSearchActivity.kt
index c9232ba8b..d76d4864d 100644
--- a/app/src/main/java/org/mozilla/fenix/widget/VoiceSearchActivity.kt
+++ b/app/src/main/java/org/mozilla/fenix/widget/VoiceSearchActivity.kt
@@ -34,6 +34,11 @@ class VoiceSearchActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ if (Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).resolveActivity(packageManager) == null) {
+ finish()
+ return
+ }
+
// Retrieve the previous intent from the saved state
previousIntent = savedInstanceState?.get(PREVIOUS_INTENT) as Intent?
if (previousIntent.isForSpeechProcessing()) {
diff --git a/app/src/main/res/anim/full_rotation.xml b/app/src/main/res/anim/full_rotation.xml
new file mode 100644
index 000000000..eefb51740
--- /dev/null
+++ b/app/src/main/res/anim/full_rotation.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/color/pager_dot.xml b/app/src/main/res/color/pager_dot.xml
new file mode 100644
index 000000000..c34fa0ffc
--- /dev/null
+++ b/app/src/main/res/color/pager_dot.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_download_default.xml b/app/src/main/res/drawable/ic_download_default.xml
new file mode 100644
index 000000000..c7387d54b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_download_default.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_file_type_apk.xml b/app/src/main/res/drawable/ic_file_type_apk.xml
new file mode 100644
index 000000000..e68e42a68
--- /dev/null
+++ b/app/src/main/res/drawable/ic_file_type_apk.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_file_type_audio_note.xml b/app/src/main/res/drawable/ic_file_type_audio_note.xml
new file mode 100644
index 000000000..0d1e65b73
--- /dev/null
+++ b/app/src/main/res/drawable/ic_file_type_audio_note.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_file_type_default.xml b/app/src/main/res/drawable/ic_file_type_default.xml
new file mode 100644
index 000000000..f76b8b185
--- /dev/null
+++ b/app/src/main/res/drawable/ic_file_type_default.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_file_type_document.xml b/app/src/main/res/drawable/ic_file_type_document.xml
new file mode 100644
index 000000000..5d1dd8325
--- /dev/null
+++ b/app/src/main/res/drawable/ic_file_type_document.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_file_type_image.xml b/app/src/main/res/drawable/ic_file_type_image.xml
new file mode 100644
index 000000000..45fed07fe
--- /dev/null
+++ b/app/src/main/res/drawable/ic_file_type_image.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_file_type_video.xml b/app/src/main/res/drawable/ic_file_type_video.xml
new file mode 100644
index 000000000..881a5c728
--- /dev/null
+++ b/app/src/main/res/drawable/ic_file_type_video.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_file_type_zip.xml b/app/src/main/res/drawable/ic_file_type_zip.xml
new file mode 100644
index 000000000..75ad4adc0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_file_type_zip.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_onboarding_avatar_anonymous_large.xml b/app/src/main/res/drawable/ic_onboarding_avatar_anonymous_large.xml
new file mode 100644
index 000000000..422897fd9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_onboarding_avatar_anonymous_large.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/pager_dot.xml b/app/src/main/res/drawable/pager_dot.xml
new file mode 100644
index 000000000..869199def
--- /dev/null
+++ b/app/src/main/res/drawable/pager_dot.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/browser_gesture_wrapper.xml b/app/src/main/res/layout/browser_gesture_wrapper.xml
deleted file mode 100644
index 4de55e61f..000000000
--- a/app/src/main/res/layout/browser_gesture_wrapper.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/collection_header.xml b/app/src/main/res/layout/collection_header.xml
index f78455a0e..e2d7af88d 100644
--- a/app/src/main/res/layout/collection_header.xml
+++ b/app/src/main/res/layout/collection_header.xml
@@ -6,7 +6,6 @@
android:id="@+id/collections_header_text"
android:layout_width="wrap_content"
android:layout_height="48dp"
- android:layout_marginStart="4.5dp"
android:gravity="center_vertical"
android:text="@string/collections_header"
android:textAppearance="@style/HeaderTextStyle" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/component_downloads.xml b/app/src/main/res/layout/component_downloads.xml
new file mode 100644
index 000000000..a6bcbe35e
--- /dev/null
+++ b/app/src/main/res/layout/component_downloads.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/component_tabstray_fab.xml b/app/src/main/res/layout/component_tabstray_fab.xml
index 8ea5ee9bb..2e9ac3a17 100644
--- a/app/src/main/res/layout/component_tabstray_fab.xml
+++ b/app/src/main/res/layout/component_tabstray_fab.xml
@@ -9,6 +9,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
+ android:scrollbars="none"
android:layout_margin="16dp"
android:backgroundTint="@color/accent_normal_theme"
android:contentDescription="@string/add_tab"
diff --git a/app/src/main/res/layout/component_top_sites.xml b/app/src/main/res/layout/component_top_sites.xml
index c0031d108..b9e0861f8 100644
--- a/app/src/main/res/layout/component_top_sites.xml
+++ b/app/src/main/res/layout/component_top_sites.xml
@@ -2,12 +2,19 @@
+
diff --git a/app/src/main/res/layout/component_top_sites_pager.xml b/app/src/main/res/layout/component_top_sites_pager.xml
new file mode 100644
index 000000000..f6f66d7fd
--- /dev/null
+++ b/app/src/main/res/layout/component_top_sites_pager.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/download_list_item.xml b/app/src/main/res/layout/download_list_item.xml
new file mode 100644
index 000000000..7e9ddafd4
--- /dev/null
+++ b/app/src/main/res/layout/download_list_item.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/about_libraries_activity.xml b/app/src/main/res/layout/fragment_about_libraries.xml
similarity index 64%
rename from app/src/main/res/layout/about_libraries_activity.xml
rename to app/src/main/res/layout/fragment_about_libraries.xml
index bf23bb437..58cea5d9d 100644
--- a/app/src/main/res/layout/about_libraries_activity.xml
+++ b/app/src/main/res/layout/fragment_about_libraries.xml
@@ -3,23 +3,16 @@
- 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/. -->
-
-
+ android:orientation="vertical"
+ tools:context="org.mozilla.fenix.settings.about.AboutLibrariesFragment">
-
+
diff --git a/app/src/main/res/layout/fragment_browser.xml b/app/src/main/res/layout/fragment_browser.xml
index 328f9bedd..09c23b747 100644
--- a/app/src/main/res/layout/fragment_browser.xml
+++ b/app/src/main/res/layout/fragment_browser.xml
@@ -2,52 +2,81 @@
-
+ android:layout_height="match_parent">
-
+ android:layout_height="match_parent">
-
-
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toTopOf="@+id/loginSelectBar"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:context="browser.BrowserFragment">
+
-
-
-
+
+
+
+
+
+
-
+
+
+
+
+
+
+
-
-
+
diff --git a/app/src/main/res/layout/fragment_downloads.xml b/app/src/main/res/layout/fragment_downloads.xml
new file mode 100644
index 000000000..11fc15df2
--- /dev/null
+++ b/app/src/main/res/layout/fragment_downloads.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index cb7c95c3b..ffcb7ad2f 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -46,8 +46,8 @@
android:id="@+id/wordmark"
android:layout_width="wrap_content"
android:layout_height="40dp"
- android:layout_marginStart="28dp"
- android:layout_marginTop="56dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginTop="18dp"
android:layout_marginBottom="32dp"
android:adjustViewBounds="true"
android:clickable="false"
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
index d85e21d64..6a7e8be88 100644
--- a/app/src/main/res/layout/fragment_search.xml
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -63,7 +63,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@id/search_suggestions_onboarding"
- android:layout="@layout/search_suggestions_onboarding"
+ android:layout="@layout/search_suggestions_hint"
app:layout_constraintBottom_toTopOf="@id/awesomeBar_barrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
diff --git a/app/src/main/res/layout/fragment_search_dialog.xml b/app/src/main/res/layout/fragment_search_dialog.xml
index 4a2abe039..6a6686f05 100644
--- a/app/src/main/res/layout/fragment_search_dialog.xml
+++ b/app/src/main/res/layout/fragment_search_dialog.xml
@@ -30,7 +30,7 @@
app:layout_constraintTop_toTopOf="parent"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/no_collections_message.xml b/app/src/main/res/layout/no_collections_message.xml
index a37d36062..472259061 100644
--- a/app/src/main/res/layout/no_collections_message.xml
+++ b/app/src/main/res/layout/no_collections_message.xml
@@ -2,42 +2,63 @@
-
+ android:padding="16dp">
+ app:fontFamily="@font/metropolis_semibold"
+ app:layout_constraintEnd_toStartOf="@id/remove_collection_placeholder"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/no_collections_header" />
-
+ android:visibility="gone"
+ app:icon="@drawable/ic_tab_collection"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/no_collections_description" />
+
diff --git a/app/src/main/res/layout/onboarding_automatic_signin.xml b/app/src/main/res/layout/onboarding_automatic_signin.xml
index d96855dcc..2b88fd85d 100644
--- a/app/src/main/res/layout/onboarding_automatic_signin.xml
+++ b/app/src/main/res/layout/onboarding_automatic_signin.xml
@@ -22,7 +22,7 @@
tools:text="@string/onboarding_firefox_account_auto_signin_header_2" />
Ubrir vinclos en aplicacions
+
+ Chestor de descargas externoComplementos
+
+ Notificacions
+
Sincronizar agora
@@ -562,6 +567,13 @@
No i hai garra historial
+
+
+ No bi ha descargas aquí
+
+ %1$d seleccionaus
+
Lo sentimos. %1$s no puede cargar ixa pachina.
@@ -962,9 +974,10 @@
Tiens preguntas sobre lo redisenyo de %s? Quiers saber qué ha cambiau?Obtiene respuestas aquí
-
+
Quita-le lo millor provecho a %s.
+
+ Saber-ne mas
@@ -1443,6 +1456,9 @@
Inicia sesión pa sincronizar
+
+ No i hai garra pestanya ubierta
+
S’ha arribau a lo limite de puestos principals.
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index d67b32965..985254e4e 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -139,15 +139,9 @@
امسح ضوئيًا
-
- الاختصاراتإعدادات محرّك البحث
-
- ابحث باستعمال
-
- الآن فقط ابحث باستعمال:املأ الرابط من الحافظة
@@ -254,8 +248,6 @@
أدوات المطوّرينالتنقيح عن بعد عبر USB
-
- اعرض اختصارات البحثاعرض اقتراحات البحث
@@ -424,6 +416,8 @@
الألسنة المفتوحةجلسة خاصة
+
+ الألسنة الخاصةأضِف لسانًا
@@ -578,9 +572,15 @@
أضِف مجلدًااختر مجلدًا
+
+ ما من علامات هناحُذف %1$s
+
+ حُذفت العلامات
+
+ يحذف المجلدات المحددةتراجَع
@@ -595,6 +595,14 @@
صفحة الإعدادات السريعةموصى به
+
+ أدِر تصاريح الموقع
+
+ امسح التصاريح
+
+ امسح التصريح
+
+ امسح التصاريح عن كل المواقعالتشغيل التلقائي
@@ -605,6 +613,8 @@
محجوبةمسموح بها
+
+ حجبه أندرويدالاستثناءات
@@ -614,6 +624,10 @@
اسمح بالصوت والڤِديو
+
+ احجب الصوت فقط
+
+ احجب الصوت والڤِديومفعّل
@@ -1001,9 +1015,7 @@
يوجد بالفعل جلسة ولوج باسم المستخدم هذا
-
- الاتصال بحساب Firefox.
-
+
صِلْ جهازا آخر.من فضلك فعّل مزامنة الألسنة.
diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml
index 96ef236a3..9aba29ae5 100644
--- a/app/src/main/res/values-ast/strings.xml
+++ b/app/src/main/res/values-ast/strings.xml
@@ -15,10 +15,10 @@
Gueta o introduz una direición
- Equi van amosase les llingüetes abiertes.
+ Equí van amosase les llingüetes abiertes.
- Equi van amosase les llingüetes abiertes.
+ Equí van amosase les llingüetes abiertes.1 llingüeta abierta. Toca pa cambiar a otra.
@@ -27,9 +27,14 @@
%1$d esbillaes
+
+ Coleición nuevaNome
+
+ Esbilla d\'una coleición
+
%1$s ta producíu por Adam Novak.
@@ -126,6 +131,8 @@
Cola potencia de %1$sMou de llector
+
+ Zarrar la vista de llectorAbrir nuna aplicación
@@ -247,6 +254,8 @@
Sincroniza los marcadores, l\'historial y muncho más cola to cuenta de FirefoxCuenta de Firefox
+
+ Reconéutate pa siguir cola sincronizaciónLlingua
@@ -279,6 +288,9 @@
Complementos
+
+ Avisos
+
Sincronizar agora
@@ -364,6 +376,11 @@
Activación de Sync
+
+ Aniciar sesión pa reconeutar
+
+ Desaniciar la cuenta
+
firefox.com/pair]]>
@@ -473,6 +490,9 @@
%1$s (en privao)
+
+ Guardar
+
Desaniciar l\'historial
@@ -503,6 +523,10 @@
Nun hai nada
+
+
+ Equí nun hai descargues
+
Perdona pero %1$s nun pue cargar esa páxina.
@@ -659,6 +683,8 @@
Esbillóse %d llingüeta¡Guardáronse les llingüetes!
+
+ ¡Guardóse la coleición!¡Guardóse la llingüeta!
@@ -690,6 +716,8 @@
Unviar a tolos preseos
+
+ Reconexón con SyncConeutar otru preséu
@@ -879,8 +907,7 @@
¿Tienes entrugues tocante al rediseñu de %s?¿Quies saber qué camudó?Consigui rempuestes equí
-
+
Aprovecha %s al máximu.Aniciando sesión…
diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml
index a39a72233..c93fa1a72 100644
--- a/app/src/main/res/values-az/strings.xml
+++ b/app/src/main/res/values-az/strings.xml
@@ -1,9 +1,5 @@
-
- Məxfi Firefox Preview
-
- Firefox Preview (Məxfi)Digər seçimlər
@@ -13,23 +9,17 @@
Məxfi səyahəti söndürÜnvanı daxil et və ya axtar
-
- Açıq vərəq yoxdurAçıq vərəqləriniz burada göstəriləcək.Məxfi sessiyadasınız
-
- %1$s tətbiqdən çıxdığınızda və ya bütün məxfi səyahət vərəq və pəncərələrini qapatdığınızda axtarış və səyahət tarixçənizi təmizləyir. Bu sizi saytlar və internet provayderiniz üçün anonim etməsə də,
- onlayn məlumatlarınızı bu cihazı işlədən digər şəxslərdən saxlamağınızı asanlaşdırır.Məxfi səyahət haqqında olan əfsanələrSessiyanı sil
-
+
Ana ekranınızdan məxfi vərəqləri açmaq üçün qısayol əlavə edin.
@@ -84,8 +74,6 @@
Yeni vərəqKolleksiyaya saxla
-
- Saytla bağlı problem xəbər etPaylaş
@@ -99,7 +87,7 @@
%1$s Tərəfindən
-
+
Oxuyucu vörünüşüTətbiqdə aç
@@ -119,14 +107,8 @@
Skanla
-
- QısayollarAxtarış mühərriyi tənzimləmələri
-
- Bununla axtar:
-
- Bu dəfə bununla axtarın:Buferdəki keçidi doldur
@@ -223,8 +205,6 @@
Tərtibatçı alətləriUSB ilə məsafəli sazlama
-
- Axtarış qısayollarını göstərAxtarış təkliflərini göstər
@@ -273,10 +253,6 @@
Son uğurlu olan: %sSon uğurlu olan: heç vaxt
-
- %s - %s %s
@@ -373,8 +349,6 @@
Oxuma siyahısıAxtar
-
- KitabxanaTənzimləmələr
@@ -401,8 +375,6 @@
Bütün vərəqləri qapatVərəqləri paylaş
-
- Kolleksiyaya saxlaVərəq menyusu
@@ -523,8 +495,6 @@
%1$s silindi
-
- Seçilən əlfəcinlər silinirGERİ AL
@@ -576,14 +546,11 @@
Sönülü
+
KolleksiyalarKolleksiya menyusu
-
- Kolleksiya yoxdur
-
- Kolleksiyalarınız burada göstəriləcəklər.Vərəqləri seç
@@ -702,8 +669,6 @@
Şrift ölçüsü
-
- Açıq Vərəqlər%d vərəq
@@ -721,8 +686,6 @@
ÇərəzlərSayt icazələri
-
- Səyahət tarixçəsiÇıx
@@ -747,14 +710,15 @@
Firefox-a daxil olSync aktivdir
-
- StandartTənzimləmələri açMəxfiliyiniz
+
+ Qapat
+
Səyahətə başlayın
@@ -762,6 +726,21 @@
Mövzunuzu seçin
+
+ Avtomatik
+
+ Tünd mövzu
+
+ Açıq mövzu
+
+
+ Vərəqlər göndərildi!
+
+ Vərəq göndərildi!
+
+ Göndərilə bilmədi
+
+ Kodu skanlaE-poçt ilə daxil ol
@@ -778,28 +757,14 @@
İzlənmədən səyahət edinƏtraflı öyrən
-
- Standart
-
- Standart (məsləhətlidir)
-
- Tarazlaşdırılmış qoruma və verimlilik.
-
- Səhifələr normal yüklənəcəklər, amma daha az izləyici əngəllənəcək.Standart izlənmə qorumasında nələr əngəllənirSərt
-
- Sərt (Ön seçilən)
-
- Sərt (məsləhətlidir)Sərt izlənmə qorumasında nələr əngəllənirFərdi
-
- Hansı izləyici və skriptlərin əngəllənəcəyini seçinFərdi izlənmə qorumasında nələr əngəllənir
@@ -1015,4 +980,5 @@
Bu hesabı silmək istədiyinizə əminsiniz?Sil
-
+
+
diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml
index 06d96eecf..b4ba83a56 100644
--- a/app/src/main/res/values-br/strings.xml
+++ b/app/src/main/res/values-br/strings.xml
@@ -22,6 +22,29 @@
%1$s ivinell digor. Stokit evit mont dʼun ivinell all.
+
+ %1$d diuzet
+
+ Ouzhpennañ un dastumad nevez
+
+ Anv
+
+ Diuzañ an dastumad
+
+ Kuitaat ar mod lies-diuzañ
+
+ Enrollañ an ivinelloù diuzet en dastumad
+
+ %1$s diuzet
+
+ %1$s diziuzet
+
+ Kuitaet ar mod lies-diuzañ
+
+ Aet er mod lies-diuzañ, diuzit ivinelloù evit o enrollañ en un dastumad
+
+ Diuzet
+
Gant Adam Novak eo produet %1$s.
@@ -141,8 +164,8 @@
Cʼhwilerviñ
-
- Lusker klask
+
+ Lusker enklaskArventennoù al lusker klask
@@ -271,9 +294,14 @@
Arventennoù ar gontDigeriñ ereoù en arloadoù
+
+ Ardoer pellgargañ diavaezAskouezhioù
+
+ Rebuzadurioù
+
Goubredañ bremañ
@@ -499,6 +527,9 @@
%1$s (Mod prevez)
+
+ Enrollañ
+
Dilemel ar roll istor
@@ -537,6 +568,13 @@
Roll istor ebet amañ
+
+
+ Pellgargadur ebet amañ
+
+ %1$d diuzet
+
Digarezit. %1$s ne cʼhall ket kargañ ar bajenn.
@@ -941,9 +979,10 @@
Goulennoù hoc’h eus a-zivout ar %s nevez? Fellout a ra deoc’h gouzout pezh a zo bet cheñchet?Amañ emañ ar respontoù
-
+
Tennit gounid eus %s.
+
+ Gouzout hiroc’h
@@ -1413,9 +1452,7 @@
Un titour kennaskañ gant an anv arveriad-mañ a zo dioutañ endeo.
-
- Kennaskañ gant ur gont Firefox
-
+
Kennaskañ un trevnad all.Adkennaskit mar plij ganeoc’h.
@@ -1429,6 +1466,9 @@
Kennaskañ evit goubredañ
+
+ Ivinell digor ebet
+
Tizhet eo bet ar vevenn lec’hiennoù
@@ -1437,13 +1477,4 @@
Mat, komprenet am eus
-
-
- Berradennoù
-
- Klask gant
-
- Evit ar wech-mañ, klask gant:
-
- Berradennoù klask
diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml
index 707c68ec7..41dfee9b1 100644
--- a/app/src/main/res/values-bs/strings.xml
+++ b/app/src/main/res/values-bs/strings.xml
@@ -37,6 +37,8 @@
Označeno %1$s
+
+ Neizabrano %1$sIzašao iz režima s više izbora
@@ -164,8 +166,8 @@
Skeniraj
-
- Pretraživač
+
+ PretraživačPostavke pretraživača
@@ -294,6 +296,8 @@
Postavke računaOtvori linkove u aplikacijama
+
+ Vanjski menadžer preuzimanjaAdd-oni
@@ -1442,9 +1446,7 @@
Prijava sa tim korisničkim imenom već postoji
-
- Povežite se sa Firefox računom.
-
+
Povežite drugi uređajPonovo potvrdite identitet.
@@ -1465,13 +1467,4 @@
OK, razumijem
-
-
- Prečice
-
- Traži na
-
- Ovaj put, traži na:
-
- Prikaži prečice za pretraživanje
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index 6966c984a..37c4b6bb0 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -305,9 +305,14 @@
Obre els enllaços en les aplicacions
+
+ Gestor de baixades externComplements
+
+ Notificacions
+
Sincronitza ara
@@ -576,6 +581,13 @@
No hi ha informació d’historial
+
+
+ No hi ha cap baixada
+
+ %1$d seleccionades
+
El %1$s no pot carregar aquesta pàgina.
@@ -983,9 +995,10 @@
Teniu preguntes sobre el redisseny del %s? Voleu saber què ha canviat?Vegeu les respostes aquí
-
+
Traieu tot el profit al %s.
+
+ Més informació
@@ -1476,6 +1489,9 @@
Inicia la sessió per sincronitzar
+
+ No hi ha cap pestanya oberta
+
S’ha arribat al límit de llocs principals
diff --git a/app/src/main/res/values-cak/strings.xml b/app/src/main/res/values-cak/strings.xml
index e059ad54e..2077d9d33 100644
--- a/app/src/main/res/values-cak/strings.xml
+++ b/app/src/main/res/values-cak/strings.xml
@@ -27,6 +27,16 @@
%1$s ruwi\' ejaqon. Tachapa\' richin nak\'ëx ruwi\'.
+
+ %1$d xcha\'
+
+ Titz\'aqatisäx k\'ak\'a\' mol
+
+ B\'i\'aj
+
+
+ Ticha\' mol
+
%1$s b\'anon ruma Adam Novak.
@@ -151,14 +161,10 @@
Tiwachib\'ëx
-
- Choj okemRunuk\'ulem kanob\'äl
-
- Tikanöx pa
- Wakami tikanöx rik\'in:
+ Wakami tikanöx rik\'in:Titz\'ajb\'äl ri ximonel pa ri molwuj
@@ -270,8 +276,6 @@
Taq rusamajib\'al nuk\'unelNäj chojmirisanem pa USB
-
- Kek\'ut retal pitz\'b\'äl richin nikanöxKek\'ut pe ri taq chilab\'enïk richin yakanon
@@ -524,6 +528,9 @@
%1$s (Ichinan Rub\'anikil)
+
+ Tiyak
+
Tiyuj natab\'äl
@@ -649,7 +656,7 @@
Xyuj %1$s
-
+
Taq yaketal xeyujTITZOLÏX
@@ -1458,9 +1465,7 @@ Achi\'el: \nhttps://www.google.com/search?q=%sK\'o chik jun tikirib\'äl molojri\'ïl rik\'in re b\'i\'aj re\'
-
- Tok rik\'in jun Rub\'i\' Rutaqoya\'l Firefox
-
+
Tokisäx jun chik okisab\'älTajuxub\'ej chik awi\'.
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index f024f2eb6..15d2075e3 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -174,6 +174,8 @@
Naskenovat
+
+ VyhledávačNastavení vyhledávače
@@ -306,9 +308,14 @@
Otevírat odkazy v aplikacích
+
+ Externí správce stahováníDoplňky
+
+ Oznámení
+
Synchronizovat
@@ -588,6 +595,13 @@
Zatím nemáte žádnou historii prohlížení
+
+
+ Žádná stahování
+
+ Vybráno stahování: %1$d
+
Promiňte, aplikace %1$s nemůže tuto stránku načíst.
@@ -993,9 +1007,10 @@
Zajímají vás novinky v přepracované aplikaci %s? Chcete vědět, co se změnilo?Zde najdete všechny odpovědi
-
+
Využijte aplikaci %s naplno.
+
+ Zjistit více
@@ -1486,6 +1501,9 @@
Přihlásit se k synchronizaci
+
+ Žádné otevřené panely
+
Dosažen limit počtu top stránek
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index ca734fe80..162056241 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -314,6 +314,9 @@
Add-ons
+
+ Benachrichtigungen
+
Jetzt synchronisieren
@@ -587,6 +590,13 @@
Keine Chronik vorhanden
+
+
+ Keine Downloads vorhanden
+
+ %1$d ausgewählt
+
Es tut uns leid. %1$s kann diese Seite nicht laden.
@@ -747,10 +757,8 @@
Sammlungen-Menü
-
- Sammeln Sie die Dinge, die Ihnen wichtig sind
- Gruppieren Sie ähnliche Suchanfragen, Websites und Tabs, um später schnell darauf zugreifen zu können.
+ Sammeln Sie die Dinge, die für Sie wichtig sind. \nGruppieren Sie ähnliche Suchanfragen, Websites und Tabs, um später schnell darauf zugreifen zu können.Tabs auswählen
@@ -1015,9 +1023,10 @@
Haben Sie Fragen zu den neu gestalteten %s? Möchten Sie wissen, was sich geändert hat?Hier erhalten Sie Antworten
-
- Machen Sie das Beste aus %s.
+
+ Beginnen Sie mit dem Synchronisieren von Lesezeichen, Passwörtern und mehr mittels Ihres Firefox-Kontos.
+
+ Weitere Informationen
@@ -1505,6 +1514,9 @@
Zum Synchronisieren anmelden
+
+ Keine offenen Tabs
+
Obergrenze für wichtige Seiten erreicht
@@ -1513,4 +1525,6 @@
Ok, verstanden
-
+
+ Entfernen
+
diff --git a/app/src/main/res/values-dsb/strings.xml b/app/src/main/res/values-dsb/strings.xml
index 7ba1b7351..08f5c7fc7 100644
--- a/app/src/main/res/values-dsb/strings.xml
+++ b/app/src/main/res/values-dsb/strings.xml
@@ -308,6 +308,9 @@
Dodanki
+
+ Powěźeńki
+
Něnto synchronizěrowaś
@@ -576,6 +579,13 @@
How žedna historija njejo
+
+
+ Žedne ześěgnjenja how
+
+ Wubrane: %1$d
+
Bóžko %1$s njamóžo toś ten bok zacytaś.
@@ -732,10 +742,8 @@
ZběrkiMeni zběrkow
-
- Gromaźćo wěcy, kótarež su wam wažne
- Zrědujśo pódobne pytanja, sedła a rejtariki za póznjejšy malsny pśistup.
+ Zběrajśo wěcy, kótarež su wam wažne.\nZrědujśo pódobne pytanja a rejtariki za malsny pśistup pózdźej.Rejtariki wubraś
@@ -982,9 +990,10 @@
Maśo pšašanja wó nowo wugótowanem %s? Cośo wěźeś, což jo se změniło?How dostanjośo wótegrona
-
- Wuwónoźćo nejlěpše z %s.
+
+ Synchronizěrujśo něnto cytańske znamjenja, gronidła a wěcej ze swójim kontom Firefox.
+
+ Dalšne informacije
@@ -1477,6 +1486,9 @@
Pla Sync pśizjawiś
+
+ Žedne wócynjone rejtarki
+
Limit za wažne sedła dostany
@@ -1485,4 +1497,6 @@
W pórěźe, som zrozměł
-
+
+ Wótwónoźeś
+
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 99839de8b..70992b589 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -34,6 +34,14 @@
Επιλογή συλλογής
+
+ Τέλος λειτουργίας πολλαπλής επιλογής
+
+ Αποθήκευση επιλεγμένων καρτελών στη συλλογή
+
+
+ Τέλος λειτουργίας πολλαπλής επιλογής
+
Το %1$s αναπτύσσεται από τη Adam Novak.
@@ -226,6 +234,10 @@
Προσθήκη συντόμευσης ιδιωτικής περιήγησηςΠροσβασιμότητα
+
+ Προσαρμοσμένος διακομιστής λογαριασμού Firefox
+
+ Προσαρμοσμένος διακομιστής SyncΛογαριασμός
@@ -255,6 +267,8 @@
Εργαλεία προγραμματιστήΑπομακρυσμένος εντοπισμός σφαλμάτων μέσω USB
+
+ Εμφάνιση μηχανών αναζήτησηςΕμφάνιση προτάσεων αναζήτησης
@@ -271,6 +285,8 @@
Ρυθμίσεις λογαριασμούΆνοιγμα συνδέσμων σε εφαρμογές
+
+ Εξωτερική διαχείριση λήψεωνΠρόσθετα
@@ -296,6 +312,10 @@
Συγχρονισμός…
+
+ Αποτυχία συγχρονισμού. Τελευταία επιτυχία: %s
+
+ Αποτυχία συγχρονισμού. Τελευταίος συγχρονισμός: ποτέΤελευταίος συγχρονισμός: %s
@@ -308,6 +328,8 @@
Ληφθείσες καρτέλες
+
+ Ειδοποιήσεις για καρτέλες που λαμβάνονται από άλλες συσκευές Firefox.Ελήφθη καρτέλα
@@ -320,19 +342,29 @@
Προστασία από καταγραφήΠροστασία από καταγραφή
+
+ Φραγή περιεχομένου και σεναρίων που σας παρακολουθούν στο διαδίκτυοΕξαιρέσεις
+
+ Ενεργοποίηση για όλες τις σελίδεςΜάθετε περισσότεραΤηλεμετρία
+
+ Δεδομένα χρήσης και τεχνικά δεδομέναΔεδομένα μάρκετινγκΠειράματα
+
+ Επιτρέπει στη Mozilla την εγκατάσταση και συλλογή δεδομένων για πειραματικές λειτουργίες
+
+ Αναφορά καταρρεύσεωνΥπηρεσία τοποθεσίας Mozilla
@@ -341,6 +373,8 @@
Ενεργοποίηση Sync
+
+ Σάρωση κωδικού σύζευξης στο Firefox για υπολογιστήΣύνδεση
@@ -595,8 +629,13 @@
Άκυρο URLΚανένας σελιδοδείκτης εδώ
+
+ Το %1$s διαγράφηκεΟι σελιδοδείκτες διαγράφηκαν
+
+ Διαγραφή επιλεγμένων φακέλωνΑΝΑΙΡΕΣΗ
@@ -645,6 +684,8 @@
ΑνενεργόΑποδοχή ήχου και βίντεο
+
+ Η αναπαραγωγή ήχων/βίντεο θα γίνεται σε Wi-FiΦραγή ήχου μόνο
@@ -661,6 +702,8 @@
Μενού συλλογήςΣυλλέξτε όλα όσα έχουν σημασία για εσάς
+
+ Ομαδοποιήστε παρόμοιες αναζητήσεις, σελίδες και καρτέλες για γρήγορη πρόσβαση αργότερα.Επιλέξτε καρτέλες
@@ -681,6 +724,12 @@
Επιλέχθηκε %d καρτέλα
+
+ Οι καρτέλες αποθηκεύτηκαν!
+
+ Η συλλογή αποθηκεύτηκε!
+
+ Η καρτέλα αποθηκεύτηκε!Κλείσιμο
@@ -718,8 +767,12 @@
Εκτός σύνδεσηςΣύνδεση άλλης συσκευής
+
+ Για να στείλετε μια καρτέλα, συνδεθείτε στο Firefox σε άλλη μία τουλάχιστον συσκευή.Το κατάλαβα
+
+ Αδυναμία κοινοποίησης σε αυτή την εφαρμογήΑποστολή σε συσκευή
@@ -826,6 +879,10 @@
Διαγραφή δεδομένων περιήγησηςΔιαγραφή δεδομένων περιήγησης στην έξοδο
+
+ Διαγράφει αυτόματα τα δεδομένα περιήγησης όταν επιλέγετε "Έξοδος" από το κύριο μενού
+
+ Διαγράφει αυτόματα τα δεδομένα περιήγησης όταν επιλέγετε \"Quit\" από το κύριο μενούΈξοδος
@@ -847,17 +904,29 @@
Το Firefox Preview είναι πλέον το Firefox Nightly
+
+
+ Το Firefox Nightly ενημερώνεται κάθε βράδυ και διαθέτει νέες, πειραματικές λειτουργίες.
+ Ωστόσο, ενδέχεται να είναι λιγότερο σταθερό. Κάντε λήψη του beta προγράμματος περιήγησής μας για μια πιο σταθερή εμπειρία.Λήψη του Firefox για Android BetaΤο Firefox Nightly έχει μετακινηθεί
+
+
+ Αυτή η εφαρμογή δεν θα λαμβάνει πλέον ενημερώσεις ασφαλείας. Σταματήστε τη χρήση αυτής της εφαρμογής και μεταβείτε στο νέο Nightly.
+ \n\nΓια να μεταφέρετε τους σελιδοδείκτες, τις συνδέσεις και το ιστορικό σας σε άλλη εφαρμογή, δημιουργήστε ένα λογαριασμό Firefox.Εναλλαγή στο νέο NightlyΤο Firefox Nightly έχει μετακινηθεί
+
+
+ Αυτή η εφαρμογή δεν θα λαμβάνει πλέον ενημερώσεις ασφαλείας. Αποκτήστε το νέο Nightly και σταματήστε τη χρήση αυτής της εφαρμογής.
+ \n\nΓια να μεταφέρετε τους σελιδοδείκτες, τις συνδέσεις και το ιστορικό σας σε άλλη εφαρμογή, δημιουργήστε ένα λογαριασμό Firefox.Αποκτήστε το νέο Nightly
@@ -872,6 +941,11 @@
Γνωρίστε το %sΔείτε τι νέο υπάρχει
+
+ Έχετε ερωτήσεις σχετικά με το επανασχεδιασμένο %s; Θέλετε να μάθετε τι έχει αλλάξει;
+
+ Λάβετε απαντήσεις εδώΑξιοποιήστε στο έπακρο το %s.
@@ -888,6 +962,9 @@
Αυτόματο απόρρητο
+
+ Οι ρυθμίσεις απορρήτου και ασφάλειας αποκλείουν ιχνηλάτες, κακόβουλο λογισμικό και εταιρείες που σας ακολουθούν.Τυπική (προεπιλογή)
@@ -900,6 +977,8 @@
Άνοιγμα ρυθμίσεωνΤο απόρρητό σας
+
+ Διαβάστε τη σημείωση απορρήτου μαςΚλείσιμο
@@ -918,6 +997,10 @@
Φωτεινό θέμα
+
+ Οι καρτέλες απεστάλησαν!
+
+ Η καρτέλα απεστάλη!Δεν ήταν δυνατή η αποστολή
@@ -933,11 +1016,18 @@
Χρήση email
+
+ Το Firefox θα σταματήσει να συγχρονίζεται με το λογαριασμό σας, αλλά δεν θα διαγράψει τα δεδομένα περιήγησης από αυτή τη συσκευή.
+
+ Το %s θα σταματήσει να συγχρονίζεται με το λογαριασμό σας, αλλά δεν θα διαγράψει τα δεδομένα περιήγησης από αυτή τη συσκευή.ΑποσύνδεσηΑκύρωση
+
+ Δεν είναι δυνατή η επεξεργασία προεπιλεγμένων φακέλων
+
Ρυθμίσεις προστασίας
@@ -951,12 +1041,22 @@
Μάθετε περισσότεραΤυπική (προεπιλογή)
+
+ Φραγή λιγότερων ιχνηλατών. Οι σελίδες θα φορτώνονται κανονικά.
+
+ Τι αποκλείει η τυπική προστασία από καταγραφήΑυστηρή
+
+ Φραγή περισσότερων ιχνηλατών, διαφημίσεων και αναδυόμενων παραθύρων. Οι σελίδες φορτώνονται ταχύτερα, αλλά ορισμένα μέρη ενδέχεται να μην λειτουργούν.
+
+ Τι αποκλείει η αυστηρή προστασία από καταγραφήΠροσαρμοσμένηΕπιλέξτε ιχνηλάτες και σενάρια για αποκλεισμό.
+
+ Τι αποκλείει η προσαρμοσμένη προστασία από καταγραφήCookies
@@ -980,6 +1080,9 @@
CryptominersFingerprinters
+ Αποκλείεται
+
+ ΕπιτρέπεταιΙχνηλάτες κοινωνικών δικτύων
@@ -990,9 +1093,20 @@
FingerprintersΠεριεχόμενο καταγραφής
+
+ Η προστασία είναι ΕΝΕΡΓΗ για αυτή τη σελίδα
+
+ Η προστασία είναι ΑΝΕΝΕΡΓΗ για αυτή τη σελίδα
+
+ Η ενισχυμένη προστασία από καταγραφή είναι ανενεργή για αυτή τη σελίδα
+
+ Πλοήγηση προς τα πίσωΤα δικαιώματά σας
+
+ Βιβλιοθήκες ανοιχτού κώδικα που χρησιμοποιούμεΤι νέο υπάρχει στο %sΚωδικός πρόσβασης
+
+ Εισάγετε ξανά το PIN σας
+
+ Ξεκλειδώστε για να δείτε τις αποθηκευμένες συνδέσεις σαςΑυτή η σύνδεση δεν είναι ασφαλής. Οι λογαριασμοί που εισάγονται εδώ ενδέχεται να παραβιαστούν.
@@ -1110,16 +1228,22 @@
Αντιγραφή ονόματος χρήστηΑντιγραφή ιστοσελίδας
+
+ Άνοιγμα σελίδας στο πρόγραμμα περιήγησηςΕμφάνιση κωδικού πρόσβασηςΑπόκρυψη κωδικού πρόσβασης
+
+ Ξεκλειδώστε για να δείτε τις αποθηκευμένες συνδέσεις σαςΑργότεραΡύθμιση τώραΞεκλειδώστε τη συσκευή σας
+
+ Ζουμ σε όλες τις ιστοσελίδεςΌνομα (Α-Ω)
@@ -1148,14 +1272,20 @@
Νήμα αναζήτησης προς χρήση
+
+ Αντικαταστήστε το ερώτημα με “%s”. Παράδειγμα:\nhttps://www.google.com/search?q=%sΜάθετε περισσότερα
+
+ Λεπτομέρειες προσαρμοσμένης μηχανής αναζήτησηςΣύνδεσμος "Μάθετε περισσότερα"Εισάγετε όνομα μηχανής αναζήτησης
+
+ Η μηχανή αναζήτησης με το όνομα “%s” υπάρχει ήδη.Εισάγετε νήμα αναζήτησης
@@ -1164,8 +1294,15 @@
Δημιουργήθηκε to %s
+
+ Το %s αποθηκεύτηκε
+
+ Το %s διαγράφηκε
+
Καλώς ορίσατε στο νέο %s
+
+ Σας περιμένει ένα πλήρως ανασχεδιασμένο πρόγραμμα περιήγησης, με βελτιωμένη απόδοση και λειτουργίες που θα σας βοηθήσουν να κάνετε περισσότερα στο διαδίκτυο.\n\nΠαρακαλούμε περιμένετε ενώ ενημερώνουμε το %s με τοΕνημέρωση του %s…
@@ -1175,6 +1312,8 @@
Κωδικοί πρόσβασης
+
+ Για να το επιτρέψετε:1. Μεταβείτε στις Ρυθμίσεις Android
@@ -1213,6 +1352,12 @@
ΔιαγραφήΕπιλογές σύνδεσης
+
+ Το επεξεργάσιμο πεδίο κειμένου της διεύθυνσης ιστού της σύνδεσης.
+
+ Το επεξεργάσιμο πεδίο κειμένου για το όνομα χρήστη της σύνδεσης.
+
+ Το επεξεργάσιμο πεδίο κειμένου για τον κωδικό πρόσβασης της σύνδεσης.Αποθήκευση αλλαγών στη σύνδεση.
@@ -1237,9 +1382,16 @@
Παρακαλούμε ενεργοποιήστε το συγχρονισμό καρτελών.
+
+ Δεν έχετε καμία άλλη καρτέλα ανοικτή στο Firefox σε άλλες σας συσκευές.
+
+ Δείτε μια λίστα καρτελών από τις άλλες συσκευές σας.Σύνδεση στο Sync
+
+
+ Έχετε φτάσει το ανώτατο όριο κορυφαίων ιστοσελίδωνOK, το κατάλαβα
diff --git a/app/src/main/res/values-en-rCA/strings.xml b/app/src/main/res/values-en-rCA/strings.xml
index 8ab0d7737..f9dc8a512 100644
--- a/app/src/main/res/values-en-rCA/strings.xml
+++ b/app/src/main/res/values-en-rCA/strings.xml
@@ -306,6 +306,9 @@
Add-ons
+
+ Notifications
+
Sync now
@@ -572,6 +575,13 @@
No history here
+
+
+ No downloads here
+
+ %1$d selected
+
Sorry. %1$s can’t load that page.
@@ -725,10 +735,8 @@
CollectionsCollection menu
-
- Collect the things that matter to you
- Group together similar searches, sites, and tabs for quick access later.
+ Collect the things that matter to you.\nGroup together similar searches, sites, and tabs for quick access later.Select Tabs
@@ -974,9 +982,10 @@
Have questions about the redesigned %s? Want to know what’s changed?Get answers here
-
- Get the most out of %s.
+
+ Start syncing bookmarks, passwords, and more with your Firefox account.
+
+ Learn more
@@ -1462,6 +1471,9 @@
Sign in to sync
+
+ No open tabs
+
Top site limit reached
@@ -1470,4 +1482,6 @@
OK, Got It
-
+
+ Remove
+
diff --git a/app/src/main/res/values-es-rAR/strings.xml b/app/src/main/res/values-es-rAR/strings.xml
index d8a8028b4..84f04ed49 100644
--- a/app/src/main/res/values-es-rAR/strings.xml
+++ b/app/src/main/res/values-es-rAR/strings.xml
@@ -313,6 +313,9 @@
Complementos
+
+ Notificaciones
+
Sincronizar ahora
@@ -592,6 +595,13 @@
Aquí no hay historial
+
+
+ No hay descargas aquí
+
+ Se seleccionó %1$d
+
Disculpá. %1$s no puede cargar esa página.
@@ -1000,9 +1010,10 @@
¿Tenés preguntas sobre el rediseño de %s? ¿Querés saber qué cambió?Las respuestas están aquí
-
+
Aprovechá %s al máximo.
+
+ Conocer más
@@ -1493,6 +1504,9 @@
Inicia sesión para sincronizar
+
+ No hay pestañas abiertas
+
Alcanzaste el límite para sitios importantes
diff --git a/app/src/main/res/values-es-rCL/strings.xml b/app/src/main/res/values-es-rCL/strings.xml
index 7480aadbd..3ea221ac3 100644
--- a/app/src/main/res/values-es-rCL/strings.xml
+++ b/app/src/main/res/values-es-rCL/strings.xml
@@ -306,6 +306,9 @@
Complementos
+
+ Notificaciones
+
Sincronizar ahora
@@ -575,6 +578,13 @@
Sin historial aquí
+
+
+ No hay descargas aquí
+
+ %1$d seleccionadas
+
Lo sentimos. %1$s no puede cargar esa página.
@@ -728,10 +738,8 @@
ColeccionesMenú de colecciones
-
- Recolecta lo que te importa
- Agrupa búsquedas, sitios y pestañas similares para acceder a ellos rápidamente.
+ Recolecta las cosas que te importan.\nAgrupa las búsquedas, los sitios y las pestañas similares para acceder a ellas rápidamente.Seleccionar pestañas
@@ -977,10 +985,11 @@
¿Tiene preguntas sobre el %s rediseñado? ¿Quieres saber qué ha cambiado?Obtenga respuestas aquí
-
- Saca el máximo provecho a %s.
+
+ Empieza a sincronizar marcadores, contraseñas y más con tu cuenta de Firefox.
+
+ Aprender más
@@ -1471,6 +1480,9 @@
Conectarse para sincronizar
+
+ No hay pestañas abiertas
+
Límite de sitios frecuentes alcanzado
@@ -1479,4 +1491,6 @@
Ok, ¡ya caché!
-
+
+ Eliminar
+
diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml
index 7d67d25fd..538a8e7c7 100644
--- a/app/src/main/res/values-es-rMX/strings.xml
+++ b/app/src/main/res/values-es-rMX/strings.xml
@@ -24,6 +24,29 @@
%1$s pestañas abiertas. Tocar para cambiar de pestaña.
+
+ %1$d seleccionadas
+
+ Agregar nueva colección
+
+ Nombre
+
+ Seleccionar colección
+
+ Salir del modo de selección múltiple
+
+ Guardar pestañas seleccionadas en la colección
+
+ %1$s seleccionada
+
+ %1$s no seleccionada
+
+ Has salido del modo de selección múltiple
+
+ Has ingresado al modo de selección múltiple, selecciona pestañas para guardar en una colección
+
+ Seleccionadas
+
%1$s es producido por Adam Novak.
@@ -146,8 +169,12 @@
Escanear
+
+ Motor de búsquedaConfiguración del buscador
+
+ Esta vez, buscar con:Pegar enlace del portapapeles
@@ -257,6 +284,8 @@
Herramientas de desarrolladorDepuración remota vía USB
+
+ Mostrar motores de búsquedaMostrar sugerencias de búsqueda
@@ -273,9 +302,14 @@
Configuración de la cuentaAbrir enlaces en aplicaciones
+
+ Administrador de descargas externoComplementos
+
+ Notificaciones
+
Sincronizar ahora
@@ -501,6 +535,9 @@
%1$s (Modo Privado)
+
+ Guardar
+
Eliminar historial
@@ -539,6 +576,13 @@
No hay ningún historial
+
+
+ No hay descargas aquí
+
+ %1$d seleccionadas
+
Lo sentimos. %1$s no puede cargar esa página.
@@ -566,6 +610,8 @@
Seleccionar carpeta¿Estás seguro de querer eliminar esta carpeta?
+
+ %s eliminará los elementos seleccionados.Se eliminó %1$s
@@ -622,6 +668,8 @@
Se eliminó %1$sSe eliminaron los marcadores
+
+ Eliminar carpetas seleccionadasDESHACER
@@ -689,10 +737,8 @@
ColeccionesMenú de la colección
-
- Colecciona las cosas que te importan
- Agrupa búsquedas, sitios y pestañas similares para un acceso rápido después.
+ Recolecta las cosas que te importan.\nAgrupa las búsquedas, los sitios y las pestañas similares para acceder a ellas rápidamente.Seleccionar pestañas
@@ -715,6 +761,8 @@
%d pestaña seleccionada¡Pestañas guardadas!
+
+ ¡Colección guardada!¡Pestaña guardada!
@@ -819,6 +867,10 @@
DENEGAR¿Seguro que quieres eliminar %1$s?
+
+ Eliminar esta pestaña va a eliminar toda la colección. Puedes crear nuevas colecciones en cualquier momento.
+
+ ¿Eliminar %1$s?Eliminar
@@ -934,9 +986,10 @@
¿Tienes preguntas sobre el rediseño de %s? ¿Quieres saber qué ha cambiado?Obtén respuestas aquí
-
- Sácale el mejor provecho a %s.
+
+ Empieza a sincronizar marcadores, contraseñas y más con tu cuenta de Firefox.
+
+ Saber más
@@ -1220,6 +1273,8 @@
Los inicios de sesión y contraseñas no serán guardados para estos sitios.
+
+ Eliminar todas las excepcionesBuscar inicios de sesión
@@ -1258,6 +1313,8 @@
Copiar nombre de usuarioCopiar sitio
+
+ Abrir sitio en el navegadorMostrar contraseña
@@ -1420,6 +1477,9 @@
Iniciar sesión para sincronizar
+
+ No hay pestañas abiertas
+
Límite de sitios frecuentes alcanzado
@@ -1428,4 +1488,6 @@
Vale, entendido
+
+ Eliminar
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index dd98faa36..4e646582d 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -562,6 +562,10 @@
No hay ningún historial
+
+ %1$d seleccionado
+
Lo sentimos. %1$s no puede cargar esa página.
@@ -986,9 +990,10 @@
¿Tienes preguntas sobre el rediseño de %s? ¿Quieres saber qué ha cambiado?Obtén respuestas aquí
-
+
Sácale el máximo provecho a %s.
+
+ Aprender más
@@ -1480,6 +1485,9 @@
Iniciar sesión para sincronizar
+
+ No hay pestañas abiertas
+
Límite de sitios frecuentes alcanzado
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index 5d7c3b9fc..27ebe2ac4 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -25,6 +25,30 @@
Irekitako %1$s fitxa. Sakatu fitxaz aldatzeko.
+
+ %1$d hautatuta
+
+ Gehitu bilduma berria
+
+ Izena
+
+ Hautatu bilduma
+
+ Irten hautapen anitzeko modutik
+
+ Gorde hautatutako fitxak bilduman
+
+ %1$s hautatuta
+
+ %1$s desautatuta
+
+ Hautapen anitzeko modutik irtenda
+
+
+ Hautapen anitzeko moduan sartuta, hautatu fitxak bilduman gordetzeko
+
+ Hautatuta
+
%1$s Adam Novakk egina da.
@@ -108,7 +132,7 @@
Fitxa berria
- Gorde bildumara
+ Gorde bildumanPartekatu
@@ -151,14 +175,12 @@
Eskaneatu
-
- Lasterbideak
+
+ Bilaketa-motorraBilaketa-motorren ezarpenak
-
- Bilatu honekin
- Oraingoan, bilatu honekin:
+ Oraingoan, bilatu honekin:Osatu lotura arbeletik
@@ -266,8 +288,8 @@
Garatzaile-tresnakUSB bidezko urruneko arazketa
-
- Erakutsi bilaketarako lasterbideak
+
+ Erakutsi bilaketa-motorrakErakutsi bilaketa-iradokizunak
@@ -286,9 +308,14 @@
Ireki loturak aplikazioetan
+
+ Kanpoko deskarga-kudeatzaileaGehigarriak
+
+ Jakinarazpenak
+
Sinkronizatu orain
@@ -468,7 +495,7 @@
Irekitako fitxak
- Gorde bildumara
+ Gorde bildumanPartekatu fitxa guztiak
@@ -506,7 +533,7 @@
Uneko saioaren irudia
- Gorde bildumara
+ Gorde bildumanEzabatu bilduma
@@ -521,6 +548,9 @@
%1$s (modu pribatua)
+
+ Gorde
+
Ezabatu historia
@@ -559,6 +589,13 @@
Historiarik ez hemen
+
+
+ Deskargarik ez hemen
+
+ %1$d hautatuta
+
Sentitzen dugu. %1$s(e)k ezin du orri hori kargatu.
@@ -586,6 +623,8 @@
Hautatu karpetaZiur zaude karpeta hau ezabatu nahi duzula?
+
+ %s(e)k hautatutako elementuak ezabatuko ditu%1$s ezabatuta
@@ -640,8 +679,10 @@
%1$s ezabatuta
-
+
Laster-markak ezabatuta
+
+ Hautatutako karpetak ezabatzenDESEGIN
@@ -737,6 +778,8 @@
Fitxa %d hautatutaFitxak gordeta!
+
+ Bilduma gordeta!Fitxa gordeta!
@@ -842,6 +885,10 @@
UKATUZiur zaude %1$s bilduma ezabatu nahi duzula?
+
+ Fitxa hau ezabatzean bilduma osoa ere ezabatu egingo da. Uneoro sor ditzakezu bildumak.
+
+ %1$s ezabatu?Ezabatu
@@ -898,8 +945,6 @@
Nabigatze-datuak automatikoki ezabatzen ditu menu nagusian "Irten" hautatzerakoanNabigatze-datuak automatikoki ezabatzen ditu menu nagusian \"Irten\" hautatzerakoan
-
- Nabigatze-historiaIrten
@@ -959,9 +1004,10 @@
Birdiseinatutako %s(r)i buruzko galderak dituzu? Zer aldatu den jakin nahi duzu?Eskuratu erantzunak hemen
-
+
Atera %s(r)i ahalik eta zuku gehiena.
+
+ Argibide gehiago
@@ -1244,6 +1290,8 @@
Gorde gabeko saio-hasiera eta pasahitzak hemen erakutsiko dira.Gune hauetarako ez da saio-hasiera eta pasahitzik gordeko.
+
+ Ezabatu salbuespen guztiakBilatu saio-hasierak
@@ -1282,6 +1330,8 @@
Kopiatu erabiltzaile-izenaKopiatu gunea
+
+ Ireki gunea nabigatzaileanErakutsi pasahitza
@@ -1434,9 +1484,7 @@
Erabiltzaile-izen hori badago lehendik ere
-
- Konektatu Firefoxen kontu batekin.
-
+
Konektatu beste gailu bat.Autentifikatu berriro mesedez.
@@ -1450,6 +1498,9 @@
Hasi saioa sinkronizatzeko
+
+ Irekitako fitxarik ez
+
Gune erabilienen mugara iritsi da
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 241fd57f1..783a9c0d3 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -313,6 +313,9 @@
Lisäosat
+
+ Ilmoitukset
+
Synkronoi nyt
@@ -584,6 +587,13 @@
Ei historiaa täällä
+
+
+ Ei latauksia
+
+ %1$d valittu
+
Pahoittelut, %1$s ei voi ladata tätä sivua.
@@ -740,10 +750,8 @@
KokoelmatKokoelmavalikko
-
- Kerää yhteen sinulle tärkeät asiat
- Kokoa yhteen samanlaiset haut, sivustot ja välilehdet nopeaa käyttöä varten.
+ Kerää merkitykselliset asiat yhteen.\nRyhmitä samanlaiset haut, sivustot ja välilehdet nopeaa käyttöä varten.Valitse välilehdet
@@ -995,9 +1003,10 @@
Kysymyksiä liittyen uudistettuun %siin? Haluatko tietää, mikä kaikki on muuttunut?Vastauksia on tarjolla täällä
-
- Ota kaikki irti %sista.
+
+ Aloita kirjanmerkkien, salasanojen ja paljon muun synkronointi Firefox-tilillä.
+
+ Lue lisää
@@ -1488,6 +1497,9 @@
Kirjaudu sisään synkronoidaksesi
+
+ Ei avoimia välilehtiä
+
Ykkössivustojen määrä täynnä
@@ -1496,4 +1508,6 @@
Selvä
-
+
+ Poista
+
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index d539a5934..c957fd851 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -311,6 +311,9 @@
Modules complémentaires
+
+ Notifications
+
Synchroniser maintenant
@@ -585,6 +588,13 @@
Pas d’historique
+
+
+ Aucun téléchargement ici
+
+ %1$d sélectionné(s)
+
%1$s n’a pas pu charger cette page.
@@ -743,10 +753,8 @@
Menu de la collection
-
- Rassemblez ce qui compte pour vous
- Regroupez des recherches, des sites et des onglets similaires pour y accéder rapidement plus tard.
+ Rassemblez ce qui compte pour vous\nCollectez des recherches, des sites et des onglets similaires pour un accès rapide plus tard.Sélectionner des onglets
@@ -1014,10 +1022,11 @@ Cependant, il peut être moins stable. Téléchargez la version bêta de notre n
Vous avez des questions sur le nouveau %s ? Vous voulez savoir ce qui a changé ?Trouvez des réponses ici
-
- Tirez le meilleur parti de %s.
+
+ Synchronisez les marque-pages, mots de passe et plus encore avec votre compte Firefox.
+
+ En savoir plus
@@ -1505,6 +1514,9 @@ Cependant, il peut être moins stable. Téléchargez la version bêta de notre n
Se connecter pour synchroniser
+
+ Aucun onglet ouvert
+
Nombre maximal de sites atteint
@@ -1513,4 +1525,6 @@ Cependant, il peut être moins stable. Téléchargez la version bêta de notre n
J’ai compris
-
+
+ Supprimer
+
diff --git a/app/src/main/res/values-fy-rNL/strings.xml b/app/src/main/res/values-fy-rNL/strings.xml
index fd87b5f30..d175b3a24 100644
--- a/app/src/main/res/values-fy-rNL/strings.xml
+++ b/app/src/main/res/values-fy-rNL/strings.xml
@@ -25,6 +25,29 @@
%1$s iepen ljepblêden. Tik om tusken ljepblêden te wikseljen.
+
+ %1$d selektearre
+
+ Nije kolleksje tafoegje
+
+ Namme
+
+ Kolleksje selektearje
+
+ Multiseleksjemodus ferlitte
+
+ Selektearre ljepblêden yn kolleksje bewarje
+
+ %1$s selektearre
+
+ Seleksje %1$s ûngedien makke
+
+ Multiseleksjemodus ferlitten
+
+ Multiseleksjemodus aktivearre, selektearje ljepblêden om yn in kolleksje te bewarjen
+
+ Selektearre
+
%1$s is makke troch Adam Novak.
@@ -150,8 +173,8 @@
Scanne
-
- Sykmasine
+
+ SykmasineYnstellingen sykmasine
@@ -282,9 +305,14 @@
Keppelingen iepenje yn apps
+
+ Eksterne downloadbehearderAdd-ons
+
+ Notifikaasjes
+
No syngronisearje
@@ -510,6 +538,9 @@
%1$s (priveemodus)
+
+ Bewarje
+
Skiednis wiskje
@@ -548,6 +579,13 @@
Gjin skiednis hjir
+
+
+ Gjin downloads hjir
+
+ %1$d selektearre
+
Sorry. %1$s kin dy side net lade.
@@ -701,10 +739,8 @@
KolleksjesKolleksjesmenu
-
- Sammelje de dingen dy\'t wichtich foar jo binne
- Groepearje fergelykbere sykopdrachten, websites en ljepblêden foar flugge tagong letter.
+ Sammelje de saken dy\'t wichtich foar jo binne.\nGroepearje fergelykbere sykopdrachten, websites en ljepblêden foar snelle tagong letter.Ljepblêden selektearje
@@ -952,9 +988,10 @@
Hawwe jo fragen oer it opnij ûntwurpen %s? Wolle jo witte wat der wizige is?Hjir fine jo antwurden
-
- Haal it measte út %s.
+
+ Start mei syngronisearjen fan blêdwizers, wachtwurden en mear mei jo Firefox-account.
+
+ Mear ynfo
@@ -1431,9 +1468,7 @@
Der bestiet al in oanmelding mei dy brûkersnamme
-
- Ferbine mei in Firefox-account.
-
+
In oar apparaat ferbine.Graach opnij autentisearje.
@@ -1447,6 +1482,9 @@
Oanmelde om te syngronisearjen
+
+ Gjin iepen ljepblêden
+
Limyt foar topwebsites berikke
@@ -1455,13 +1493,6 @@
OK, begrepen
-
-
- Fluchkeppelingen
-
- Sykje mei
-
- Diskear sykje mei:
-
- Sykfluchkeppelingen toane
-
+
+ Fuortsmite
+
diff --git a/app/src/main/res/values-gn/strings.xml b/app/src/main/res/values-gn/strings.xml
index a1a65ff9b..a74d35146 100644
--- a/app/src/main/res/values-gn/strings.xml
+++ b/app/src/main/res/values-gn/strings.xml
@@ -311,6 +311,9 @@
Moĩmbaha
+
+ Ñemomarandu
+
Embojuehe ko’ág̃a
@@ -584,6 +587,13 @@
Ndaipóri tembiasakue
+
+
+ Ndaipóri ñemboguejy ápe
+
+ %1$d poravopyre
+
Rombyasy. %1$s nomyanyhẽkuaái pe kuatiarogue.
@@ -742,10 +752,8 @@
Ñembyatyha poravorã
-
- Embyaty umi mba’ekuéra ehayhu añetéva
- Embyaty jehekaha, tenda ha tendayke ojueheguáva eike pya’eve hag̃ua ag̃ave.
+ Embyaty umi mba’e ehayhuvéva.\nEmboaty umi jeheka, tenda ha tendayke ojueheguáva eike hag̃ua pype pya’e.Eiporavo tendayke
@@ -998,9 +1006,10 @@
¿Ereko porandu %s jejapojey rehegua? ¿Eikuaase mba’épa iñambuéra’e?Umi ñembohovái oĩ ápe
-
- Eguenohẽ %s-gui eikotevẽva.
+
+ Embojuehe techaukaha, ñe’ẽñemi ha hetave ne mba’ete Firefox pegua ndive.
+
+ Eikuaave
@@ -1500,6 +1509,9 @@
Eñepyrũ tembiapo embojuehe hag̃ua
+
+ Ndaipóri tendayke ijurujáva
+
Ehupytýma hu’ã tenda mba’guasúvape g̃uarã
@@ -1508,4 +1520,6 @@
Oĩma, aikumby
-
+
+ Mboguete
+
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 554768c6d..2cfb83497 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -307,6 +307,9 @@
Dodaci
+
+ Obavijesti
+
Sinkroniziraj sada
@@ -581,6 +584,13 @@
Nema povijesti
+
+
+ Ovdje nema preuzimanja
+
+ Odabrano: %1$d
+
Oprosti. %1$s ne može učitati tu stranicu.
@@ -738,10 +748,8 @@
Izbornik za zbirke
-
- Skupi stvari koje su ti bitne
- Grupiraj slične pretrage, stranice i kartice za brži pristup kasnije.
+ Skupi stvari koje su ti bitne.\nGrupiraj slična pretraživanja, stranice i kartice za kasniji brzi pristup.Odaberi kartice
@@ -988,9 +996,10 @@
Imaš pitanja o redizajniranom %su? Želiš znati što se promijenilo?Potraži odgovore ovdje
-
- Iskoristi sve prednosti %sa
+
+ Počni sinkronizirati zabilješke, lozinke i ostalo sa svojim Firefox računom.
+
+ Saznaj više
@@ -1488,6 +1497,9 @@
Prijavi se za sinkronizaciju
+
+ Nema otvorenih kartica
+
Dostignuto ograničenje za omiljene stranice
@@ -1496,4 +1508,6 @@
U redu, shvaćam
-
+
+ Ukloni
+
diff --git a/app/src/main/res/values-hsb/strings.xml b/app/src/main/res/values-hsb/strings.xml
index ff758d28d..5d006590f 100644
--- a/app/src/main/res/values-hsb/strings.xml
+++ b/app/src/main/res/values-hsb/strings.xml
@@ -308,6 +308,9 @@
Přidatki
+
+ Zdźělenki
+
Nětko synchronizować
@@ -577,6 +580,13 @@
Tu žana historija njeje
+
+
+ Žane sćehnjenja tu
+
+ Wubrane: %1$d
+
Bohužel %1$s njemóže tutu stronu začitać.
@@ -731,10 +741,8 @@
ZběrkiMeni zběrkow
-
- Zběrajće wěcy, kotrež su wam wažne
- Zeskupće podobne pytanja, sydła a rajtarki za pozdźiši spěšny přistup.
+ Zběrajće wěcy, kotrež su wam wažne.\nZeskupće podobne pytanja a rajtarki za spěšny přistup pozdźišo.Rajtarki wubrać
@@ -982,9 +990,10 @@
Maće prašenja wo nowo wuhotowanym %s? Chceće wědźeć, štož je so změniło?Tu dóstanjeće wotmołwy
-
- Wućehńće najlěpše z %s.
+
+ Synchronizujće nětko zapołožki, hesła a wjace ze swojim kontom Firefox.
+
+ Dalše informacije
@@ -1475,6 +1484,9 @@
Pola Sync přizjewić
+
+ Žane wočinjene rajtarki
+
Limit za wažne sydła docpěty
@@ -1483,4 +1495,6 @@
W porjadku, sym zrozumił
-
+
+ Wotstronić
+
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 4293cb553..05081c1e7 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -310,6 +310,9 @@
Kiegészítők
+
+ Értesítések
+
Szinkronizálás most
@@ -580,6 +583,13 @@
Nincsenek előzmények
+
+
+ Nincs itt letőltés
+
+ %1$d kiválasztva
+
Sajnáljuk. A %1$s nem tudja betölteni az oldalt.
@@ -735,10 +745,8 @@
GyűjteményekGyűjtemény menü
-
- Gyűjtse össze az Önnek fontos dolgokat
- Csoportosítsa a hasonló kereséseket, webhelyeket és lapokat a későbbi gyors elérés érdekében.
+ Gyűjtse össze a számára fontos dolgokat.\nCsoportosítsa a hasonló kereséseket, webhelyeket és lapokat a későbbi gyors hozzáférés végett.Válasszon lapokat
@@ -991,9 +999,10 @@
Kérdése van az újratervezett %s böngészőről? Tudni akarja, hogy mi változott?Itt kaphat válaszokat
-
- Hozza ki a legtöbbet a %s böngészőből.
+
+ Kezdje el szinkronizálni a könyvjelzőit, jelszavait és még többet a Firefox-fiókjával.
+
+ További tudnivalók
@@ -1485,6 +1494,9 @@
Jelentkezzen be a Syncbe
+
+ Nincsenek nyitott lapok
+
Kedvenc oldalak korlátja elérve
@@ -1493,4 +1505,6 @@
Rendben, értem
-
+
+ Eltávolítás
+
diff --git a/app/src/main/res/values-hy-rAM/strings.xml b/app/src/main/res/values-hy-rAM/strings.xml
index cd1b1ac6f..f57573aba 100644
--- a/app/src/main/res/values-hy-rAM/strings.xml
+++ b/app/src/main/res/values-hy-rAM/strings.xml
@@ -49,7 +49,7 @@
Ընտրված
- %1$s-ն արտադրվում է Adam Novak-ի կողմից:
+ %1$s-ը մշակված է Adam Novak-ի կողմից:
@@ -184,9 +184,9 @@
Չթույլատրել
- Թույլատրեք որոնման առաջարկներ մասնավոր աշխատաշրջաններում:
+ Թույլատրել որոնման առաջարկներ մասնավոր աշխատաշրջաններում:
- %s-ը համօգտագործելու է այն ամենը, ինչ մուտքագրում եք հասցեի տողում՝ ձեր սկզբնական որոնիչի հետ:
+ %s-ը համօգտագործելու է այն ամենը, ինչ մուտքագրում եք հասցեի տողում ձեր սկզբնական որոնիչի հետ:Իմանալ ավելին
@@ -266,13 +266,13 @@
Հարմարեցնել
- Համաժամեցեք էջանիշները, պատմությունը և ավելին ձեր Firefox հաշվի հետ
+ Համաժամեցրեք էջանիշները, պատմությունը և ավելին ձեր Firefox հաշվի հետFirefox-ի հաշիվԿրկին կապակցվեք՝ համաժամեցումը վերսկսելու համար
- Լեզու
+ ԼեզունՏվյալների ընտրություն
@@ -303,9 +303,14 @@
Հաշվի կարգավորումներԲացել հղումները հավելվածներում
+
+ Ներբեռնումների արտաքին հավելվածՀավելումներ
+
+ Ծանուցումներ
+
Համաժամեցնել
@@ -377,7 +382,7 @@
Telemetry
- Օգտագործում և տեխնիկական տվյալներ
+ Օգտագործման և տեխնիկական տվյալներԿիսվում է արագագործության, օգտագործման, սարքակազմի և ձեր դիտարկիչի կարգավորումներով Mozilla-ի հետ՝ %1$s-ը էլ ավելի լավը դարձնելու համար
@@ -572,6 +577,13 @@
Այստեղ պատմություն չկա
+
+
+ Չկան նեևբեռնումներ
+
+ Ընտրված է %1$d-ը
+
Ներողություն. %1$s-ը չի կարող բեռնել այդ էջը:
@@ -622,7 +634,7 @@
Բացել նոր ներդիր
- Բացել Գաղտնի ներդիրով
+ Բացել Գաղտնի ներդիրումՋնջել
@@ -728,10 +740,8 @@
ՀավաքածուներՀավաքածուի ցանկ
-
- Հավաքեք ձեզ համար կարևոր բաները
- Խմբավորեք համանման որոնումները, կայքերը և ներդիրները՝ հետո արագ մատչելու համար:
+ Հավաքեք ձեզ համար կարևոր բաները:\nԽմբավորեք համանման որոնումները, կայքերը և ներդիրները՝ ավելի ուշ արագ մատչելու համար:Ընտրեք ներդիրները
@@ -894,7 +904,7 @@
%d ներդիրներ
- Պատմության և կայքի տվյալների դիտարկում
+ Դիտարկման պատմություն և կայքերի տվյալներ%d հասցեներ
@@ -906,13 +916,13 @@
Cookie-ներ
- Դուք դուրս կգրվեք կայքերի մեծ մասից
+ Դուրս կգրվեք կայքերի մեծ մասից
- Պահված պատկերներն ու ֆայլերը
+ Շտեմված պատկերներ ու ֆայլերԱզատում է պահեստային տարածք
- Կայքի թույլտվություններ
+ Կայքերի թույլտվություններՋնջել դիտարկման տվյալները
@@ -980,9 +990,10 @@
Ստացեք պատասխանները այստեղ
-
- Օգտագործեք առավելագույնը %s-ից:
+
+ Համաժամեցրեք էջանիշերը, գաղտնաբառերը և ավելին՝ ձեր Firefox հաշվով:
+
+ Իմանալ ավելին
@@ -1017,16 +1028,16 @@
- Դիրքորոշեք
+ Դիրքորոշել
- Փորձեք մի ձեռքով դիտարկումը գործիքագոտու ներքևում կամ այն տեղափոխել վերև:
+ Փորձեք մի ձեռքով դիտարկումը գործիքագոտու ներքևում կամ այն տեղափոխեք վերև:
- Դիտարկեք գաղտնի
+ Դիտարկել գաղտնի
- Մեկ անգամ բացել գաղտնի ներդիր. Հպեք %s պատկերակին:
+ Ցանկանում եք բացել գաղտնի ներդիր, հպեք %s պատկերակին:
- Ամեն անգամ բացել գաղտնի ներդիրները. Թարմացրեք ձեր գաղտնի դիտարկման կարգավորումները:
+ Ցանկանում եք ամեն անգամ բացել գաղտնի ներդիրները. Թարմացրեք գաղտնի դիտարկման կարգավորումները:Բացել կարգավորումները
@@ -1034,8 +1045,8 @@
- Մենք պատրաստել ենք %s-ը, որպեսզի տանք Ձեզ հսկողություն սահմանելու այն ամենի նկատմամբ, ինչ որ համօգտագործում եք
- առցանց և այն, ինչ դուք համօգտագործում եք մեզ հետ:
+ Մենք պատրաստել ենք %s-ը, որպեսզի Ձեզ տանք հսկողություն սահմանելը այն ամենի նկատմամբ, ինչ որ համօգտագործում եք
+ առցանց և մեզ հետ:Կարդացեք մեր գաղտնիության ծանուցումը
@@ -1076,7 +1087,7 @@
Մուտք գործեք ձեր տեսախցիկի հետ
- Փոխարենը օգտագործեք էլ. փոստ
+ Փոխարենը օգտ. էլ. փոստFirefox-ը կկանգնեցնի համաժամացումը ձեր հաշվի հետ, բայց չի ջնջվի այս սարքում ձեր դիտարկման որևէ տվյալ։
@@ -1096,7 +1107,7 @@
Դիտարկեք առանց Ձեզ հետևելու
- Ձեր տվյալները պահեք ձեզ մոտ: %s-ը ձեզ պաշտպանում է ամենատարածված հետագծիչներից, որոնք հետևում են այն ամենին, ինչ որ անում եք առցանց:
+ Ձեր տվյալները պահեք ձեզ մոտ: %s-ը պաշտպանում է ամենատարածված հետագծիչներից, որոնք հետևում են այն ամենին, ինչ որ անում եք առցանց:Իմանալ ավելին
@@ -1231,7 +1242,7 @@
Դյուրանցման անունը
- Հեշտությամբ կարող եք ավելացնել այս կայքը ձեր հեռախոսի Տնային էկրանին՝ ակնթարթային մատչելու և արագ դիտարկելու համար:
+ Հեշտությամբ կարող եք ավելացնել այս կայքը ձեր հեռախոսի Տնային էկրանին՝ ակնթարթորեն մատչելու և արագ դիտարկելու համար:Մուտքանուններ և գաղտնաբառեր
@@ -1254,7 +1265,7 @@
Մուտք գործեք՝ համաժամեցնելու համար
- Պահպանված մուտքաանուններ
+ Պահպանված մուտքանուններՁեր պահպանած կամ համաժամեցրած մուտքանունները կցուցադրվեն այստեղ:
@@ -1289,7 +1300,7 @@
Իմանալ ավելին
- Ցանկանո՞ւմ եք, որ %s-ը պահպանի այս մուտքաանունը:
+ Ցանկանո՞ւմ եք, որ %s-ը պահպանի այս մուտքանունը:Պահպանել
@@ -1355,7 +1366,7 @@
Անուն
- Որոնել տողը՝ օգտագործելու համար
+ Օգտագործվող որոնման տողը Հարցումը փոխարինել “%s”-ով: Օրինակ՝ \nhttps://www.google.com/search?q=%s
@@ -1370,7 +1381,7 @@
«%s» անունով որոնիչ արդեն գոյություն ունի:
- Մուտքագրել որոնման տող
+ Մուտքագրեք որոնման տողՍտուգեք, որ որոնման տողը համապատասխանում է օրինակի ձևաչափին
@@ -1383,10 +1394,10 @@
%s-ը ջնջվել է
- Բարի գալուստ բոլորովին նոր %s
+ Բարի գալուստ նոր %s
- Ամբողջովին վերափոխված դիտարկիչը Ձեզ է սպասում՝ բարելավված արտադրողականությամբ և յուրահատկություններով, որոնք կօգնեն ավելին անել առցանց:\n\nԽնդրում ենք սպասել, մինչ մենք կթարմացնենք %s-ը ձեր
+ Ամբողջովին վերափոխված դիտարկիչը Ձեզ է սպասում՝ բարելավված արտադրողականությամբ և յուրահատկություններով, որոնք կօգնեն անել ավելին առցանց:\n\nԽնդրում ենք սպասել, մինչ մենք կթարմացնենք %s-ի հետևյալ բաղադրիչները՝%s-ը արդիացվում է…
@@ -1416,7 +1427,7 @@
Համոզվա՞ծ եք, որ ցանկանում եք մաքրել այս թույլտվությունը այս կայքում:
- Կայքի բացառություններ չեն
+ Բացառություններ չկանԼավագույն հոդվածներ
@@ -1431,7 +1442,7 @@
Խմբագրել
- Համոզվա՞ծ եք, որ ցանկանում եք ջնջել այս մուտքագրումը:
+ Համոզվա՞ծ եք, որ ցանկանում եք ջնջել այս մուտքանունը:Ջնջել
@@ -1456,7 +1467,7 @@
Խոսել
- Այդ անունով մուտքագրում արդեն գոյություն ունի
+ Այդ անունով մուտքանուն արդեն գոյություն ունի
@@ -1473,6 +1484,9 @@
Մուտք գործեք՝ համաժամեցնելու համար
+
+ Չկան բացված ներդիրներ
+
Լավագույն կայքերի ցանկը լրացել է
@@ -1481,4 +1495,6 @@
Հասկանալի է
-
+
+ Հեռացնել
+
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index 37ea73214..ed95c1c08 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -316,6 +316,9 @@
Pengaya
+
+ Notifikasi
+
Sinkronkan sekarang
@@ -590,6 +593,13 @@
Tidak ada riwayat di sini
+
+
+ Tidak ada unduhan di sini
+
+ %1$d terpilih
+
Maaf. %1$s tidak dapat memuat halaman.
@@ -1002,9 +1012,10 @@
Punya pertanyaan mengenai desain ulang %s? Ingin tahu apa saja yang berubah?Dapatkan jawabannya di sini
-
+
Dapatkan hasil maksimal dari %s.
+
+ Pelajari lebih lanjut
@@ -1402,7 +1413,7 @@
%s dihapus
- Selamat datang ke %s yang benar-benar baru
+ Selamat datang di %s terbaruSebuah peramban yang telah didesain ulang sepenuhnya, dengan peningkatan kinerja dan fitur untuk membantu anda dalam melakukan sesuatu secara daring.\n\nHarap tunggu selama kami memperbarui %s dengan milik anda
@@ -1491,6 +1502,9 @@
Masuk untuk sinkronisasi
+
+ Tidak ada tab terbuka
+
Batas situs teratas tercapai
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 05275476b..e27610753 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -314,6 +314,9 @@
Componenti aggiuntivi
+
+ Notifiche
+
Sincronizza adesso
@@ -589,6 +592,13 @@
Nessuna cronologia disponibile.
+
+
+ Nessun download disponibile
+
+ %1$d selezionati
+
Siamo spiacenti, non è possibile caricare la pagina in %1$s.
@@ -1017,9 +1027,10 @@
Hai domande sul nuovo %s? Vuoi scoprire che cosa è cambiato?Trova tutte le risposte qui
-
+
Ottieni il massimo da %s.
+
+ Ulteriori informazioni
@@ -1508,6 +1519,9 @@
Accedi per sincronizzare
+
+ Nessuna scheda aperta
+
Raggiunto limite per i siti principali
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index 1f434b73b..9adaa3982 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -57,7 +57,7 @@
הפעלה זו היא הפעלה פרטית
- %1$s מנקה את היסטוריית החיפוש והגלישה שלך מלשוניות פרטיות בעת סגירתן או כשהיישומון נסגר. פעולה זו אמנם לא הופכת אותך לאלמוני כלפי אתרים או ספק האינטרנט שלך, אבל כן מקלה עליך בשמירה על הפעולות המקוונות שלך מפני כל מי שמשתמש במכשיר זה.
+ %1$s מנקה את היסטוריית החיפוש והגלישה שלך מלשוניות פרטיות בעת סגירתן או כשהיישומון נסגר. פעולה זו אמנם לא הופכת אותך לאלמוני כלפי אתרים או ספק האינטרנט שלך, אבל כן מקלה עליך בשמירה על הפעולות המקוונות שלך מפני כל מי שמשתמש במכשיר זה.
מיתוסים נפוצים על גלישה פרטית
@@ -280,7 +280,7 @@
ניפוי שגיאות מרחוק דרך USB
- הצגת מנועי חפוש
+ הצגת מנועי חיפושהצגת הצעות חיפוש
@@ -304,6 +304,9 @@
תוספות
+
+ התרעות
+
סנכרון כעת
@@ -567,6 +570,13 @@
אין היסטוריה כאן
+
+
+ אין כאן הורדות
+
+ %1$d נבחרו
+
ל־%1$s אין אפשרות לטעון דף זה, עמך הסליחה.
@@ -723,10 +733,8 @@
אוספיםתפריט אוסף
-
- לאסוף את הדברים החשובים לך
- ניתן לקבץ חיפושים, אתרים ולשוניות דומים יחד כדי לגשת אליהם מהר יותר בהמשך.
+ לאסוף את הדברים החשובים לך.\nניתן לקבץ חיפושים, אתרים ולשוניות דומים יחד כדי לגשת אליהם מהר יותר בהמשך.בחירת לשוניות
@@ -975,9 +983,10 @@
יש לך שאלות על העיצוב החדש של %s? רוצה לדעת מה השתנה?התשובות כאן
-
- להוציא את המיטב מ־%s.
+
+ סנכרון סימניות, ססמאות ועוד עם חשבון ה־Firefox שלך.
+
+ מידע נוסף
@@ -1450,6 +1459,9 @@
יש להתחבר כדי לסנכרן
+
+ אין לשוניות פתוחות
+
הגעת למכסת האתרים המובילים
@@ -1458,4 +1470,6 @@
בסדר, הבנתי
-
+
+ הסרה
+
diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml
index eb46441ce..aec114dc2 100644
--- a/app/src/main/res/values-kab/strings.xml
+++ b/app/src/main/res/values-kab/strings.xml
@@ -311,6 +311,9 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
Izegrar
+
+ Ilɣa
+
Mtawi tura
@@ -582,6 +585,13 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
Ulac amazray dagi
+
+
+ Ulac isidar da
+
+ %1$d yettwafren
+
Suref-aɣ. %1$s ur izmir ara ad isali asebter-nni.
@@ -738,10 +748,8 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
TigrummiwinUmuɣ n tegrumma
-
- Sdukkel-d akk tiɣawsiwin i yesεan azal ɣur-k/m
- Segrew akk akken inadiyen, ismal d waccaren yemṣadan akken ad yishil unekcum ɣer-sen mbeεd.
+ Lqeḍ tiɣawsiwin i yesεan aẓal ɣur-k·m.\n Semlilil akk inadiyen ittcabin, ismal, d iccaren i unekcum arurad ticki.Fren iccaren
@@ -990,9 +998,10 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
Tesεiḍ asteqsi ɣef %s ittwasinflen? Tebɣiḍ ad tezreḍ acu ibeddlen?Awi-d tiririyen da
-
- Faṛes tagnit seg %s.
+
+ Bdu amtawi n ticraḍ, awalen uffiren, akked waṭas-nniḍen s umiḍan n Firefox.
+
+ Issin ugar
@@ -1487,6 +1496,9 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
Kcem akken ad yemtawi
+
+ Ulac iccaren yeldin
+
Tewwḍeḍ ɣer talast n usmel
@@ -1495,4 +1507,6 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
IH, awi-t-id
-
+
+ Kkes
+
diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml
index 687fab438..411a98a4f 100644
--- a/app/src/main/res/values-kk/strings.xml
+++ b/app/src/main/res/values-kk/strings.xml
@@ -302,6 +302,9 @@
Қосымшалар
+
+ Хабарламалар
+
Қазір синхрондау
@@ -568,6 +571,13 @@
Осында тарих жоқ
+
+
+ Жүктемелер жоқ
+
+ %1$d таңдалды
+
Кешіріңіз. %1$s бұл бетті жүктей алмайды.
@@ -975,9 +985,10 @@
Қайта жасалған %s туралы сұрақтарыңыз бар ма? Не өзгертілгенін білуді қалайсыз ба?Осы жерден жауап алыңыз
-
+
%s өнімін толықтай пайдаланыңыз.
+
+ Көбірек білу
@@ -1466,6 +1477,9 @@
Синхрондау үшін кіру
+
+ Ашық беттер жоқ
+
Үздік сайттар саны шегіне жетті
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 80a8ae735..fd97740a9 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -319,6 +319,9 @@
부가 기능
+
+ 알림
+
지금 동기화
@@ -414,7 +417,7 @@
- Sync 켜기
+ 동기화 시작하기데스크톱 Firefox에서 페어링 코드를 스캔하세요
@@ -593,6 +596,13 @@
기록 없음
+
+
+ 다운로드 없음
+
+ %1$d개 선택됨
+
죄송합니다. %1$s가 페이지를 열 수 없습니다.
@@ -755,10 +765,8 @@
모음집 메뉴
-
- 중요한 것들 수집하기
- 나중에 빠르게 접근할 수 있도록 유사한 검색, 사이트 및 탭을 모아 보세요.
+ 중요한 것들을 수집하세요.\n나중에 빠르게 액세스할 수 있도록 유사한 검색, 사이트 및 탭을 함께 그룹화하세요.탭 선택
@@ -950,7 +958,7 @@
쿠키
- 대부분의 사이트에서 로그아웃됩니다
+ 대부분 웹 사이트 자동 로그아웃캐시된 이미지와 파일
@@ -1026,9 +1034,10 @@
새롭게 디자인된 %s에 대해 궁금한 점이 있습니까? 변경된 사항을 알고 싶으십니까?여기서 답을 얻으세요
-
- %s를 최대한 활용하세요.
+
+ Firefox 계정으로 북마크, 비밀번호 등을 동기화하세요.
+
+ 더 알아보기
@@ -1050,7 +1059,7 @@
자동 개인정보 보호
- 개인 정보 및 보안 설정은 추적기, 멀웨어 및 사용자를 따라다니는 회사를 차단합니다.
+ 개인 정보 및 보안 설정은 추적기, 악성 코드 및 사용자를 따라다니는 회사를 차단합니다.표준 (기본값)
@@ -1143,7 +1152,7 @@
향상된 추적 방지 기능
- 추적되지 않는 탐색
+ 브라우저 추적 차단하기자신의 데이터를 보호하세요. %s는 온라인에서 하는 일을 추적하는 가장 일반적인 많은 추적기로부터 사용자를 보호합니다.
@@ -1523,6 +1532,9 @@
Sync에 로그인
+
+ 열린 탭이 없음
+
상위 사이트 제한에 도달
@@ -1531,4 +1543,6 @@
확인
-
+
+ 삭제
+
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index 352d3c9fa..fa9b77809 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -310,6 +310,9 @@
Utvidelser
+
+ Varslinger
+
Synkroniser nå
@@ -582,6 +585,13 @@
Ingen historikk her
+
+
+ Ingen nedlastinger her
+
+ %1$d valgt
+
Beklager. %1$s kan ikke laste den siden.
@@ -738,10 +748,8 @@
SamlingerSamlingsmeny
-
- Samle tingene som betyr noe for deg
- Grupper sammen lignende søk, nettsteder og faner for rask tilgang senere.
+ Samle tingene som betyr noe for deg.\nGrupper lignende søk, nettsteder og faner for rask tilgang senere.Velg faner
@@ -992,9 +1000,10 @@
Få svar her
-
- Få mest mulig ut av %s.
+
+ Begynn å synkronisere bokmerker, passord og mer med Firefox-kontoen din.
+
+ Les mer
@@ -1496,6 +1505,9 @@
Logg inn for å synkronisere
+
+ Ingen åpne faner
+
Grense for populære nettsteder nådd
@@ -1504,4 +1516,6 @@
OK, jeg skjønner
-
+
+ Fjern
+
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
index 29193af41..f7656c92b 100644
--- a/app/src/main/res/values-night/colors.xml
+++ b/app/src/main/res/values-night/colors.xml
@@ -7,6 +7,7 @@
@color/primary_text_dark_theme@color/secondary_text_dark_theme@color/contrast_text_dark_theme
+ @color/caption_text_dark_theme@color/foundation_dark_theme@color/above_dark_theme@color/inset_dark_theme
@@ -49,6 +50,7 @@
#00B3F4@color/search_suggestion_indicator_icon_color_dark_theme@color/search_suggestion_indicator_icon_bookmark_color_dark_theme
+ @color/accent_high_contrast_dark_theme@color/mozac_widget_favicon_background_dark_theme@color/mozac_widget_favicon_border_dark_theme
@@ -72,6 +74,8 @@
@color/top_site_title_text_dark_theme
+ #3A3944
+ #5B5B66@color/synced_tabs_separator_dark_theme
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index d98eeccaf..3642836ed 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -36,12 +36,18 @@
NaamCollectie selecteren
+
+ Multiselectiemodus verlatenGeselecteerde tabbladen in collectie opslaan%1$s geselecteerdSelectie %1$s ongedaan gemaakt
+
+ Multiselectiemodus verlaten
+
+ Multiselectiemodus geactiveerd, selecteer tabbladen om in een collectie op te slaanGeselecteerd
@@ -309,6 +315,9 @@
Add-ons
+
+ Notificaties
+
Nu synchroniseren
@@ -578,6 +587,13 @@
Geen geschiedenis hier
+
+
+ Geen downloads hier
+
+ %1$d geselecteerd
+
Sorry. %1$s kan die pagina niet laden.
@@ -732,10 +748,8 @@
CollectiesCollectiesmenu
-
- Verzamel de dingen die belangrijk voor u zijn
- Groepeer vergelijkbare zoekopdrachten, websites en tabbladen voor snelle toegang later.
+ Verzamel de zaken die voor u belangrijk zijn.\nGroepeer vergelijkbare zoekopdrachten, websites en tabbladen voor snelle toegang later.Tabbladen selecteren
@@ -985,9 +999,10 @@
Hebt u vragen over het opnieuw ontworpen %s? Wilt u weten wat er is gewijzigd?Hier vindt u antwoorden
-
- Haal het meeste uit %s.
+
+ Start met synchroniseren van bladwijzers, wachtwoorden en meer met uw Firefox-account.
+
+ Meer info
@@ -1478,6 +1493,9 @@
Aanmelden om te synchroniseren
+
+ Geen open tabbladen
+
Limiet voor topwebsites bereikt
@@ -1486,4 +1504,6 @@
OK, begrepen
-
+
+ Verwijderen
+
diff --git a/app/src/main/res/values-nn-rNO/strings.xml b/app/src/main/res/values-nn-rNO/strings.xml
index 927400c30..56f4f1eff 100644
--- a/app/src/main/res/values-nn-rNO/strings.xml
+++ b/app/src/main/res/values-nn-rNO/strings.xml
@@ -311,6 +311,9 @@
Tillegg
+
+ Varsel
+
Synkroniser no
@@ -581,6 +584,10 @@
Ingen historikk her
+
+
+ Ingen nedlastingar her
+
Orsak. %1$s klarer ikkje å laste inn denne sida.
@@ -737,10 +744,6 @@
SamlingarSamlingsmeny
-
- Samle tinga som betyr noko for deg
-
- Grupper saman liknande søk, nettstadar og faner for rask tilgang seinare.Vel faner
@@ -991,9 +994,8 @@
Få svar her
-
- Få mest muleg ut av %s.
+
+ Les meir
@@ -1487,6 +1489,9 @@
Logg inn for å synkronisere
+
+ Ingen opne faner
+
Grense for populære nettstadar nådd
@@ -1495,4 +1500,6 @@
OK, eg forstår det
-
+
+ Fjern
+
diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml
index 4a8989b75..28e403f02 100644
--- a/app/src/main/res/values-oc/strings.xml
+++ b/app/src/main/res/values-oc/strings.xml
@@ -43,6 +43,8 @@
%1$s deseleccionatSortida del mòde seleccion multipla
+
+ Mòde de seleccion multipla activat, seleccionatz los onglets d’enregistrar dins una colleccionSeleccionat
@@ -305,6 +307,9 @@
Moduls complementaris
+
+ Notificacions
+
Sincronizar ara
@@ -379,8 +384,12 @@
TelemetriaDonadas tecnicas e d’utilizacion
+
+ Parteja d’informacions sus las performàncias, lo material, l’utilizacion e las personalizacions a Mozilla per nos ajudar a melhorar %1$sDonadas marketing
+
+ Parteja de donadas sus las foncionalitats qu’utilizatz amb Leanplum, nòstre provesidor de marketing mobil.Experimentacions
@@ -572,6 +581,13 @@
Cap d’istoric
+
+
+ Pas cap de telecargament
+
+ %1$d seleccionats
+
%1$s a pas pogut cargar aquesta pagina.
@@ -734,6 +750,8 @@
Menú de colleccionAmassatz çò que compta per vos
+
+ Agropatz de recèrcas, de sites e d’onglets similaris per i accedir rapidament mai tard.Seleccionar d’onglets
@@ -797,6 +815,8 @@
Fòra linhaConnectar un periferic mai
+
+ Per enviar un onglet, connectatz-vos a vòstre compte Firefox sus almens un autre periferic.Comprés !
@@ -962,9 +982,10 @@
Descobrissètz las novetatsTrobatz de responsas aquí
-
+
Ne fasètz mai amb %s.
+
+ Ne saber maiÒc, connectatz-me
@@ -977,6 +998,8 @@
Sincro. activadaFracàs de connexion
+
+ Confidencialitat automaticaLos paramètres de confidencialitat e de seguretat blocan los traçadors, los logicials malvolents e las entrepresas que vos pistan.
@@ -1012,6 +1035,8 @@
Causissètz vòstre tèma
+
+ Estalviatz la batariá e vòstra vista en activant lo mòde fosc.Automatic
@@ -1145,6 +1170,8 @@
Plantatges
+
+ Vòstres drechesEntresenhas sus la licéncia
@@ -1400,6 +1427,9 @@
Se connectar a Sync
+
+ Cap d’onglet pas dobèrt
+
Arribat a la limita dels sites principals
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index d4e9db57c..21776d175 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -311,6 +311,9 @@
Dodatki
+
+ Powiadomienia
+
Synchronizuj
@@ -581,6 +584,13 @@
Nie ma jeszcze historii
+
+
+ Nie ma pobranych plików
+
+ Zaznaczone: %1$d
+
%1$s nie może wczytać tej strony.
@@ -989,9 +999,10 @@
Masz pytania na temat nowego interfejsu przeglądarki %s? Chcesz wiedzieć, co się zmieniło?Tutaj znajdziesz odpowiedzi
-
- W pełni wykorzystuj możliwości przeglądarki %s.
+
+ Zacznij synchronizować zakładki, hasła i nie tylko za pomocą konta Firefoksa.
+
+ Więcej informacji
@@ -1482,6 +1493,9 @@
Zaloguj się do synchronizacji
+
+ Brak otwartych kart
+
Osiągnięto limit popularnych stron
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 8d8c57f49..59e6b69b6 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -307,6 +307,9 @@
Extensões
+
+ Notificações
+
Sincronizar agora
@@ -575,6 +578,13 @@
Nenhum histórico aqui
+
+
+ Nenhum download aqui
+
+ %1$d selecionados
+
Desculpe. O %1$s não pode carregar essa página.
@@ -729,10 +739,8 @@
ColeçõesMenu de coleções
-
- Colecione o que é importante para você
- Agrupe pesquisas, sites e abas semelhantes para acesso rápido mais tarde.
+ Reúna o que é importante para você.\nAgrupe pesquisas, sites e abas semelhantes para acesso rápido mais tarde.Selecionar abas
@@ -981,9 +989,10 @@
Tem dúvidas sobre o %s reprojetado? Quer saber o que mudou?Obtenha respostas aqui
-
- Aproveite ao máximo o %s.
+
+ Comece a sincronizar favoritos, senhas e muito mais com sua Conta Firefox.
+
+ Saiba mais
@@ -1034,9 +1043,7 @@
Sua privacidade
- Projetamos o %s para lhe dar o controle sobre o que você compartilha
- online e o que compartilha conosco.
-
+ Projetamos o %s para lhe dar o controle sobre o que você compartilha online e o que compartilha conosco.Leia nosso aviso de privacidade
@@ -1477,6 +1484,9 @@
Entrar para sincronizar
+
+ Nenhuma aba aberta
+
Atingiu o limite de sites preferidos
@@ -1485,4 +1495,6 @@
OK, entendi
-
+
+ Remover
+
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index 43e07e668..f01488a5c 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -308,6 +308,9 @@
Extras
+
+ Notificações
+
Sincronizar agora
@@ -579,6 +582,13 @@
Sem histórico
+
+
+ Sem transferências aqui
+
+ %1$d selecionados
+
Desculpe. O %1$s não pode carregar essa página.
@@ -734,10 +744,8 @@
ColeçõesMenu de coleções
-
- Colecione as coisas que são importantes para si
- Agrupe pesquisas, sites e separadores semelhantes para acesso rápido mais tarde.
+ Reúna o que é importante para você.\nAgrupe pesquisas, sites e separadores semelhantes para um acesso rápido mais tarde.Selecione os separadores
@@ -984,10 +992,11 @@
Tem questões sobre o redesenhado %s? Quer saber o que mudou?Obtenha respostas aqui
-
- Tire o máximo proveito do %s.
+
+ Comece a sincronizar os marcadores, palavras-passe e muito mais com a sua conta Firefox.
+
+ Saber mais
@@ -1481,6 +1490,9 @@
Iniciar sessão para sincronizar
+
+ Sem separadores abertos
+
Atingido o limite dos sites principais
@@ -1489,4 +1501,6 @@
OK, percebi
-
+
+ Remover
+
diff --git a/app/src/main/res/values-rm/strings.xml b/app/src/main/res/values-rm/strings.xml
index 7bb6c7694..742483ec6 100644
--- a/app/src/main/res/values-rm/strings.xml
+++ b/app/src/main/res/values-rm/strings.xml
@@ -24,6 +24,29 @@
%1$s tabs averts. Tutgar per midar tab.
+
+ %1$d tschernids
+
+ Agiuntar ina nova collecziun
+
+ Num
+
+ Tscherner ina collecziun
+
+ Sortir dal modus da tscherna multipla
+
+ Memorisar ils tabs tschernids en ina collecziun
+
+ %1$s tschernì
+
+ %1$s betg tschernì
+
+ Sortì dal modus da tscherna multipla
+
+ Avert il modus da tscherna multipla, tscherner ils tabs per als memorisar en ina collecziun
+
+ Tschernì
+
%1$s vegn sviluppà da Adam Novak.
@@ -144,14 +167,12 @@
Scannar
-
- Scursanidas
+
+ Maschina da tschertgarParameters da la maschina da tschertgar
-
- Tschertgar cun
- Questa giada, tschertgar cun:
+ Questa giada, tschertgar cun:Encollar la colliaziun en l\'archiv provisoric
@@ -258,8 +279,8 @@
Utensils per sviluppadersDebugging a distanza via USB
-
- Mussar las scursanidas per tschertgas
+
+ Mussar las maschinas da tschertgarMussar propostas da tschertga
@@ -277,9 +298,14 @@
Parameters dal contoAvrir colliaziuns en apps
+
+ Administraziun da telechargiadas externaSupplements
+
+ Communicaziuns
+
Sincronisar ussa
@@ -505,6 +531,9 @@
%1$s (modus privat)
+
+ Memorisar
+
Stizzar la cronologia
@@ -543,6 +572,13 @@
Nagina cronologia
+
+
+ Naginas telechargiadas disponiblas
+
+ %1$d tschernidas
+
Perstgisa. %1$s na po betg chargiar questa pagina.
@@ -570,6 +606,8 @@
Tscherner in ordinaturVuls ti propi stizzar quest ordinatur?
+
+ %s vegn a stizzar ils elements tschernids.Stizzà %1$s
@@ -624,8 +662,10 @@
Stizzà %1$s
-
+
Stizzà ils segnapaginas
+
+ Stizzar ils ordinaturs tschernidsREVOCAR
@@ -692,10 +732,8 @@
CollecziunsMenu da la collecziun
-
- Rimnar las chaussas che t\'èn impurtantas
- Gruppescha retschertgas, paginas e tabs sumegliants per pli tard pudair acceder pli svelt.
+ Rimna quai ch\'è impurtant per tai.\nGruppescha tschertgas sumegliantas, websites e tabs per meglierar l\'access per la proxima giada.Tscherner tabs
@@ -718,6 +756,8 @@
%d tab tschernìMemorisà ils tabs!
+
+ Memorisà la collecziun!Memorisà il tab!
@@ -825,6 +865,10 @@
REFUSARVuls ti propi stizzar %1$s?
+
+ Cun stizzar quest tab vegn l\'entira collecziun stizzada. Ti pos da tut temp crear novas collecziuns.
+
+ Stizzar %1$s?Stizzar
@@ -939,9 +983,10 @@
Has ti dumondas areguard il nov %s? Vuls savair tge ch\'è sa midà?Qua datti respostas
-
- Rablar ora il maximum da %s.
+
+ Cumenza a sincronisar segnapaginas, pleds-clav ed autra rauba cun tes conto da Firefox.
+
+ Ulteriuras infurmaziuns
@@ -1231,6 +1276,8 @@
Las infurmaziuns d\'annunzia ed ils pleds-clav betg memorisads vegnan mussadas qua.Las infurmaziuns d\'annunzia ed ils pleds-clav na vegnan betg memorisads per questas paginas.
+
+ Stizzar tut las excepziunsTschertgar datas d\'annunzia
@@ -1270,6 +1317,8 @@
Copiar il num d\'utilisaderCopiar la pagina
+
+ Avrir la website en il navigaturMussar il pled-clav
@@ -1422,9 +1471,7 @@
Datas d\'annunzia cun quest num d\'utilisader existan gia
-
- Connectar cun in conto da Firefox.
-
+
Colliar in auter apparat.Re-autentifitgescha per plaschair.
@@ -1438,6 +1485,9 @@
S\'annunziar tar Sync
+
+ Nagins tabs averts
+
Cuntanschì la limita da paginas preferidas
@@ -1446,4 +1496,6 @@
OK, chapì
+
+ Allontanar
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index 5cbeb3c1f..bd5f78979 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -303,6 +303,9 @@
Suplimente
+
+ Notificări
+
Sincronizează acum
@@ -400,7 +403,7 @@
Autentificare
- Autentifică-te pentru reconectare
+ Intră în cont pentru reconectareȘterge contul
@@ -569,6 +572,13 @@
Fără istoric
+
+
+ Nu există descărcări aici
+
+ %1$d selectate
+
Ne cerem scuze. %1$s nu poate încărca pagina.
@@ -632,7 +642,7 @@
Editează dosarul
- Autentifică-te pentru a vedea marcajele sincronizate
+ Intră în cont pentru a vedea marcajele sincronizateURL
@@ -724,10 +734,8 @@
Meniu de colecții
-
- Colectează ce te interesează
- Grupează căutările similare, site-urile și filele pentru acces mai rapid ulterior.
+ Colectează ce contează pentru tine.\nGrupează laolaltă căutări, site-uri și file similare pentru acces facil ulterior.Selectează filele
@@ -782,7 +790,7 @@
Folosite recent
- Autentifică-te în Sync
+ Intră în contul SyncTrimite pe toate dispozitivele
@@ -792,7 +800,7 @@
Conectează alt dispozitiv
- Pentru a trimite o filă, autentifică-te în Firefox pe cel puțin un alt dispozitiv.
+ Pentru a trimite o filă, intră în contul Firefox de pe cel puțin un alt dispozitiv.Am înțeles
@@ -974,9 +982,10 @@
Ai întrebări despre %s reproiectat? Vrei să știi ce s-a schimbat?Obține răspunsuri aici
-
- Profită la maxim de %s.
+
+ Începe să sincronizezi marcaje, parole și multe altele din contul Firefox.
+
+ Află mai multe
@@ -986,7 +995,7 @@
Autentificare…
- Autentifică-te în Firefox
+ Intră în contul FirefoxRămâi deconectat
@@ -1068,7 +1077,7 @@
Gata de scanare
- Autentifică-te cu camera
+ Autentificare cu cameraFolosește e-mailul în schimb
@@ -1116,7 +1125,7 @@
Cookie-uri
- Elemente de urmărire ale rețelelor sociale și între site-uri
+ Elemente de urmărire de pe rețele de socializare și inter-site-uriCookie-uri de pe site-uri nevizitate
@@ -1139,7 +1148,7 @@
Permise
- Elemente de urmărire de pe rețelele de socializare
+ Elemente de urmărire de pe rețele de socializareLimitează capacitatea rețelelor de socializare de a-ți urmări activitatea de navigare pe web.
@@ -1246,7 +1255,7 @@
Reconectare
- Autentifică-te în Sync
+ Intră în contul SyncDate de autentificare salvate
@@ -1464,7 +1473,10 @@
Afișează o listă de file de pe celelalte dispozitive.
- Autentifică-te în Sync
+ Intră în contul Sync
+
+
+ Nicio filă deschisă
@@ -1474,4 +1486,6 @@
Ok, am înțeles
-
+
+ Elimină
+
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 62beb1aab..6d8f1ad48 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -225,7 +225,7 @@
Справка
- Оценить на Google Play
+ Оценить в Google PlayОставить отзывДополнения
+
+ Уведомления
+
Синхронизировать
@@ -587,6 +590,13 @@
История отсутствует
+
+
+ Здесь ещё нет загрузок
+
+ Выбрано: %1$d
+
Извините, %1$s не смог загрузить эту страницу.
@@ -745,10 +755,8 @@
Меню коллекции
-
- Собирайте то, что важно для вас
- Объединяйте похожие запросы, сайты и вкладки, для быстрого доступа к ним в дальнейшем.
+ Собирайте то, что важно для вас.\nОбъединяйте похожие запросы, сайты и вкладки для быстрого доступа в будущем.Выберите вкладки
@@ -1013,9 +1021,10 @@
Есть вопросы о переработанном %s? Хотите узнать, что изменилось?Получите ответы здесь
-
- Получите максимум от %s.
+
+ Начните синхронизировать закладки, пароли и многое другое с вашим Аккаунтом Firefox.
+
+ Узнать больше
@@ -1085,11 +1094,11 @@
Сохраните заряд аккумулятора и ваше зрение, включив тёмную тему.
- Автоматически
+ АвтоматическаяАдаптируется к настройкам вашего устройства
- Темная тема
+ Тёмная темаСветлая тема
@@ -1503,6 +1512,9 @@
Войти в Синхронизацию
+
+ Нет открытых вкладок
+
Достигнут лимит топа сайтов
@@ -1511,4 +1523,6 @@
OK, понятно
-
+
+ Убрать
+
diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml
index a0f6a9551..1626f419d 100644
--- a/app/src/main/res/values-sl/strings.xml
+++ b/app/src/main/res/values-sl/strings.xml
@@ -299,6 +299,9 @@
Dodatki
+
+ Obvestila
+
Sinhroniziraj zdaj
@@ -382,7 +385,7 @@
Poskusi
- Dovoli Mozilli namestitev in pridobivanje informacij eksperimentalnih funkcij
+ Dovoli Mozilli namestitev in pridobivanje podatkov poskusnih zmogljivostiPoročevalec o sesutju
@@ -729,10 +732,6 @@
ZbirkeMeni zbirk
-
- Zbirajte stvari, ki vam kaj pomenijo
-
- Združite podobna iskanja, spletne strani in zavihke za hitrejši dostop.Izberi zavihke
@@ -981,9 +980,8 @@
Imate vprašanja o preoblikovanem %su? Želite izvedeti, kaj se je spremenilo?Tukaj poiščite odgovore
-
- Kar najbolje izkoristite %s.
+
+ Več o tem
@@ -1480,6 +1478,9 @@
Prijava v Sync
+
+ Ni odprtih zavihkov
+
Omejitev glavnih strani je dosežena
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 10424a612..c1a44e9f5 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -306,6 +306,9 @@
Додаци
+
+ Обавештења
+
Синхронизуј сада
@@ -574,6 +577,13 @@
Овде нема историјата
+
+
+ Овде нема преузимања
+
+ Изабрано %1$d
+
Нажалост, %1$s не може учитати ту страницу.
@@ -981,9 +991,10 @@
Имате питање о новом дизајну апликације %s? Желите ли да знате шта је промењено?Овде потражите одговоре
-
+
Искористите %s у потпуности.
+
+ Сазнајте више
@@ -1478,6 +1489,9 @@
Пријавите се на Sync
+
+ Нема отворених језичака
+
Достигнуто је ограничење популарних страница
diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml
index 6b72d2b64..bd87a75ad 100644
--- a/app/src/main/res/values-su/strings.xml
+++ b/app/src/main/res/values-su/strings.xml
@@ -308,6 +308,9 @@
Émbohan
+
+ Iber
+
Singkronkeun ayeuna
@@ -578,6 +581,13 @@
Teu aya jujutan di dieu
+
+
+ Taya undeuran di dieu
+
+ %1$d dipilih
+
Hampura. %1$s teu tiasa nyungsi ieu kaca.
@@ -989,9 +999,10 @@
Boga patalekan ngeunaan rarancang anyar %s? Hoyong uninga naon anu robah?Kéngingkeun waleran di dieu
-
+
Maksimalkeun %s.
+
+ Lenyepan
@@ -1486,6 +1497,9 @@
Asup pikeun nyingkronkeun
+
+ Taya tab muka
+
Wates loka top geus kahontal
diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml
index d4d901de9..393545cef 100644
--- a/app/src/main/res/values-sv-rSE/strings.xml
+++ b/app/src/main/res/values-sv-rSE/strings.xml
@@ -311,6 +311,9 @@
Tillägg
+
+ Aviseringar
+
Synkronisera nu
@@ -584,6 +587,13 @@
Ingen historik här
+
+
+ Inga hämtningar här
+
+ %1$d markerad
+
Tyvärr. %1$s kan inte ladda den sidan.
@@ -738,10 +748,8 @@
SamlingarSamlingsmeny
-
- Samla de saker som är viktiga för dig
- Gruppera liknande sökningar, webbplatser och flikar för snabb åtkomst senare.
+ Samla de saker som är viktiga för dig.\nGruppera liknande sökningar, webbplatser och flikar för snabb åtkomst senare.Välj flikar
@@ -995,9 +1003,10 @@
Har du frågor om omgjorda %s? Vill du veta vad som har förändrats?Få svar här
-
- Få ut det mesta av %s.
+
+ Börja synkronisera bokmärken, lösenord och mer med ditt Firefox-konto.
+
+ Läs mer
@@ -1488,6 +1497,9 @@
Logga in för att synkronisera
+
+ Inga öppna flikar
+
Övre gräns för mest besökta nådd
@@ -1496,4 +1508,6 @@
Ok, jag förstår
-
+
+ Ta bort
+
diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml
index 28da2a258..16fd64f4e 100644
--- a/app/src/main/res/values-te/strings.xml
+++ b/app/src/main/res/values-te/strings.xml
@@ -308,6 +308,9 @@
పొడగింతలు
+
+ గమనింపులు
+
ఇప్పుడు సింక్ చేయి
@@ -531,7 +534,7 @@
సేకరణ పేరుమార్చు
- తెరిచివున్న ట్యాబులు
+ ట్యాబులను తెరువుతీసివేయి
@@ -581,6 +584,13 @@
ఇక్కడ చరిత్రేమీ లేదు
+
+
+ దింపుకోళ్ళేమీ లేవు
+
+ %1$d ఎంచుకున్నారు
+
క్షమించండి. %1$s ఆ పేజీని లోడు చేయలేకుంది.
@@ -741,10 +751,8 @@
సేకరణలుసేకరణ మెనూ
-
- మీకు ముఖ్యమైన విషయాలను సేకరించండి
- తర్వాత త్వరితంగా చేరుకోడానికి వీలుగా సంబంధింత వెతుకులాటలను, సైట్లను, ట్యాబులను సమూహంగా చేసుకోండి.
+ మీకు ముఖ్యమైన విషయాలను సేకరించండి.\nత్వరగా చేరుకోవడం కోసం సంబంధిత వెతుకులాటలను, సైట్లను, ట్యాబులను ఒకచోట పెట్టుకోండి.ట్యాబుల ఎంపిక
@@ -996,9 +1004,10 @@
పునఃరూపకల్పన చేసిన %s గురించి ప్రశ్నలు ఉన్నాయా? ఏమి మార్చబడిందో తెలుసుకోవాలనుకుంటున్నారా?ఇక్కడ సమాధానాలు పొందండి
-
- %s నుండి ఎక్కువ పొందండి.
+
+ మీ Firefox ఖాతాతో ఇష్టాంశాలను, సంకేతపదాలను, ఇంకా మరెన్నిటినో సింక్రనించుకోవడం మొదలుపెట్టండి.
+
+ ఇంకా తెలుసుకోండి
@@ -1494,6 +1503,9 @@
సింక్ చేయడానికి ప్రవేశించండి
+
+ తెరిచివున్న ట్యాబులు లేవు
+
మేటి సైట్ల పరిమితి చేరుకున్నారు
@@ -1502,4 +1514,6 @@
సరే, అర్థమయ్యింది
-
+
+ తొలగించు
+
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 04b775c4c..1f6f6f397 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -309,6 +309,9 @@
Eklentiler
+
+ Bildirimler
+
Şimdi eşitle
@@ -577,6 +580,13 @@
Geçmiş yok
+
+
+ Hiçbir şey indirilmemiş
+
+ %1$d indirme seçildi
+
Kusura bakmayın, %1$s bu sayfayı yükleyemedi.
@@ -982,9 +992,10 @@
Yeni %s hakkında sorularınız mı var? Nelerin değiştiğini bilmek ister misiniz?Yanıtlar burada
-
+
%s tarayıcınızdan en iyi şekilde yararlanın.
+
+ Daha fazla bilgi al
@@ -1471,6 +1482,9 @@
Sync’e giriş yapın
+
+ Açık sekme yok
+
Sık kullanılan site sınırına ulaşıldı
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 1514a0c48..5d0f6f708 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -313,6 +313,9 @@
Додатки
+
+ Сповіщення
+
Синхронізувати
@@ -584,6 +587,13 @@
Історія відсутня
+
+
+ Завантажень немає
+
+ Вибрано %1$d
+
Шкода, але %1$s не може завантажити цю сторінку.
@@ -738,10 +748,8 @@
ЗбіркиМеню збірок
-
- Зберігайте важливі для вас речі
- Групуйте подібні пошуки, сайти та вкладки для швидкого доступу.
+ Збирайте все важливе для вас. \nГрупуйте подібні пошуки, вебсайти та вкладки для швидкого доступу надалі.Виберіть вкладки
@@ -992,10 +1000,10 @@
Отримайте відповіді тут
-
- Отримайте максимум від %s.
-
+
+ Почніть синхронізувати закладки, паролі та інші дані з Обліковим записом Firefox.
+
+ Докладніше
@@ -1489,6 +1497,9 @@
Увійти до синхронізації
+
+ Немає відкритих вкладок
+
Досягнуто ліміту популярних сайтів
@@ -1497,4 +1508,6 @@
Гаразд, зрозуміло
-
+
+ Вилучити
+
diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml
index 2f7d3cf0f..4ce403420 100644
--- a/app/src/main/res/values-ur/strings.xml
+++ b/app/src/main/res/values-ur/strings.xml
@@ -306,6 +306,9 @@
ایڈ اون
+
+ اعلانات
+
ابھی sync کریں
@@ -579,6 +582,10 @@
یہاں کوئی سابقات نہیں ہے
+
+
+ یہاں کوئی ڈاؤن لوڈ نہیں
+
معاف کریں۔ %1$s اس صفحہ کو لوڈ نہیں کر سکتا ہے۔
@@ -976,8 +983,7 @@
دیکھیں نیا کیا ہےیہاں جوابات حاصل کریں
-
+
%s کا زیادہ سے زیادہ فائدہ اٹھائیں۔ہاں ، مجھے سائن ان کریں
@@ -1434,6 +1440,9 @@
sync کے لئے سائن ان کریں
+
+ کوئی کھلے ٹیب نہیں
+
اعلٰی سائٹ کی حد پوری ہو گئی
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index c4af3125c..dd21205ff 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -101,7 +101,7 @@
Dừng
- Dấu trang
+ Đánh dấuChỉnh sửa dấu trang
@@ -307,6 +307,9 @@
Tiện ích
+
+ Thông báo
+
Đồng bộ ngay
@@ -574,6 +577,13 @@
Không có lịch sử ở đây
+
+
+ Không có tải xuống ở đây
+
+ %1$d đã chọn
+
Xin lỗi. %1$s không thể tải trang đó.
@@ -728,10 +738,8 @@
Bộ sưu tậpMenu bộ sưu tập
-
- Thu thập những thứ quan trọng với bạn
- Nhóm các tìm kiếm, trang web và thẻ tương tự để truy cập nhanh sau này.
+ Thu thập những thứ quan trọng đối với bạn.\nNhóm các tìm kiếm, trang web và thẻ tương tự lại với nhau để truy cập nhanh sau này.Chọn các thẻ
@@ -976,9 +984,10 @@
Có câu hỏi nào về %s được thiết kế lại không? Bạn muốn biết những gì đã thay đổi?Nhận câu trả lời tại đây
-
- Tận dụng tối đa %s.
+
+ Bắt đầu đồng bộ hóa dấu trang, mật khẩu và nhiều hơn nữa với tài khoản Firefox của bạn.
+
+ Tìm hiểu thêm
@@ -1465,6 +1474,9 @@
Đăng nhập vào đồng bộ hóa
+
+ Không có thẻ đang mở
+
Đã đạt đến giới hạn trang web hàng đầu
@@ -1473,4 +1485,6 @@
OK, đã hiểu
-
+
+ Xóa
+
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 82ec19bf4..6e5619bab 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -318,6 +318,9 @@
附加组件
+
+ 通知
+
立即同步
@@ -592,6 +595,13 @@
无历史记录
+
+
+ 暂无下载项
+
+ 已选择 %1$d 个下载项
+
抱歉,%1$s 无法加载该页面。
@@ -752,10 +762,8 @@
收藏集菜单
-
- 有用的东西收藏在这
- 将相似的搜索查询、网站和标签页归入一组,方便以后快速访问。
+ 有用的东西收藏在这。\n将相似的搜索查询、网站和标签页归入一组,方便以后快速访问。选取标签页
@@ -1022,9 +1030,10 @@
对重新设计的 %s 有好奇的地方吗?想知道哪里变得不一样了?在此寻找答案
-
- 畅享 %s。
+
+ 使用您的 Firefox 账户开始同步书签、历史记录等数据。
+
+ 详细了解
@@ -1510,6 +1519,9 @@
登录同步服务
+
+ 没有打开的标签页
+
超出常用网站限制
@@ -1518,4 +1530,6 @@
我知道了
-
+
+ 移除
+
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 90c4203de..0a62c4431 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -314,6 +314,9 @@
附加元件
+
+ 通知
+
立即同步
@@ -586,6 +589,13 @@
沒有紀錄
+
+
+ 沒有下載的檔案
+
+ 已選擇 %1$d 個下載的檔案
+
很抱歉,%1$s 無法載入該頁面。
@@ -744,10 +754,8 @@
收藏集選單
-
- 收集對您而言重要的東西
- 將類似的搜尋、網站、分頁放在一起,方便之後快速使用。
+ 收集對您來說重要的東西。\n將類似的搜尋項目、網站、上網分頁收集起來,方便以後快速開啟。選擇分頁
@@ -1011,9 +1019,10 @@
對重新設計的 %s 有好奇的地方嗎?想知道哪裡變得不一樣了?在此尋找答案
-
- 發揮 %s 的最大威力。
+
+ 使用您的 Firefox 帳號同步書籤、密碼等資料。
+
+ 了解更多
@@ -1499,6 +1508,9 @@
登入至 Sync
+
+ 無已開啟的分頁
+
超出熱門網站限制
@@ -1507,4 +1519,6 @@
好,知道了!
-
+
+ 移除
+
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 292f24ea5..2edab8d15 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -93,4 +93,5 @@
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 3dbeaf224..8469a6d0b 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -4,6 +4,7 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ #BFBFC9#312A65#7A312A65#9059FF
@@ -15,6 +16,7 @@
#20123A@color/photonGrey50@color/primary_text_dark_theme
+ @color/photonLightGrey90#F9F9FB#E0E0E6#FFF
@@ -42,7 +44,7 @@
@color/foundation_light_theme@color/foundation_light_theme@color/foundation_light_theme
- #BFBFC9
+ @color/light_grey_50@color/accent_light_theme#C45A27#FFFDE2
@@ -84,6 +86,7 @@
#FBFBFE#A7A2B7@color/primary_text_dark_theme
+ @color/photonLightGrey70#1C1B22#32313C#32313C
@@ -150,6 +153,7 @@
#FBFBFE#A7A2B7@color/primary_text_private_theme
+ @color/photonLightGrey70#261E4B@color/photonInk50@color/photonInk50
@@ -208,6 +212,7 @@
@color/primary_text_light_theme@color/secondary_text_light_theme@color/contrast_text_light_theme
+ @color/caption_text_light_theme@color/foundation_light_theme@color/above_light_theme@color/inset_light_theme
@@ -245,7 +250,7 @@
@color/prompt_login_edit_text_cursor_color_light_theme@color/search_suggestion_indicator_icon_color_light_theme@color/search_suggestion_indicator_icon_bookmark_color_light_theme
-
+ @color/accent_bright_light_theme@color/mozac_widget_favicon_background_light_theme@color/mozac_widget_favicon_border_light_theme
@@ -271,6 +276,8 @@
@color/top_site_title_text_light_theme
+ @color/photonLightGrey30
+ @color/light_grey_50@color/synced_tabs_separator_light_theme
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 47bc4e334..6c01291ef 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -172,4 +172,5 @@
48dp
+ 16dp
diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml
index 3fdca5945..e54e27829 100644
--- a/app/src/main/res/values/preference_keys.xml
+++ b/app/src/main/res/values/preference_keys.xml
@@ -20,6 +20,7 @@
pref_key_privacy_linkpref_key_delete_browsing_datapref_key_delete_browsing_data_on_quit_preference
+ pref_key_notificationspref_key_delete_browsing_data_on_quitpref_key_delete_open_tabs_on_quitpref_key_delete_browsing_history_on_quit
@@ -57,6 +58,8 @@
pref_key_open_in_app_openedpref_key_install_pwa_openedpref_key_install_pwa_visits
+ pref_key_times_app_opened
+ pref_key_last_review_prompt_shown_timepref_key_telemetry
@@ -120,6 +123,9 @@
pref_key_auto_battery_themepref_key_follow_device_theme
+
+ pref_home_category
+
pref_key_etp_learn_morepref_key_tracking_protection_settings
@@ -165,20 +171,26 @@
pref_key_testing_stagepref_key_encryption_key_generated
+
+
pref_key_pocket_top_site_addedpref_key_top_sites_size
+
+ pref_key_top_sites_max_limit
+
+ pref_key_top_frecent_sites
-
+
pref_key_user_knows_about_pwapref_key_migrating_from_fenix_nightly_tippref_key_migrating_from_firefox_nightly_tippref_key_migrating_from_fenix_tip
- pref_key_use_new_search_experience
-
pref_key_wait_first_paint
+ pref_key_synced_tabs_tabs_tray
+
pref_key_debug_settingspref_key_open_tabs_count
@@ -193,4 +205,7 @@
pref_key_default_browserpref_key_login_exceptions
+
+ pref_key_show_collections_home
+ pref_key_temp_review_prompt
diff --git a/app/src/main/res/values/static_strings.xml b/app/src/main/res/values/static_strings.xml
index 61337c35c..85d9b556c 100644
--- a/app/src/main/res/values/static_strings.xml
+++ b/app/src/main/res/values/static_strings.xml
@@ -32,10 +32,12 @@
Secret Settings
-
- Use New Search Experience
+
+ Show Top Frequently Visited SitesWait Until First Paint To Show Page Content
+
+ Show Synced Tabs in the tabs traylink
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 41bed2780..bb4bb70f0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -78,6 +78,28 @@
Not now
+
+
+ You can set Firefox to automatically open links in apps.
+
+ Go to settings
+
+ Dismiss
+
+
+ Camera access needed. Go to Android settings, tap permissions, and tap allow.
+
+ Go to settings
+
+ Dismiss
+
+
+ Set open tabs to close automatically that haven’t been viewed in the past day, week, or month.
+
+ View options
+
+ Dismiss
+
New tab
@@ -258,6 +280,8 @@
ToolbarTheme
+
+ HomeCustomize
@@ -300,6 +324,8 @@
External download managerAdd-ons
+
+ Notifications
@@ -520,6 +546,8 @@
Open tabsRemove
+
+ Delete from history%1$s (Private Mode)
@@ -563,6 +591,13 @@
No history here
+
+
+ No downloads here
+
+ %1$d selected
+
Sorry. %1$s can’t load that page.
@@ -716,10 +751,8 @@
CollectionsCollection menu
-
- Collect the things that matter to you
- Group together similar searches, sites, and tabs for quick access later.
+ Collect the things that matter to you.\nGroup together similar searches, sites, and tabs for quick access later.Select Tabs
@@ -965,9 +998,10 @@
Have questions about the redesigned %s? Want to know what’s changed?Get answers here
-
- Get the most out of %s.
+
+ Start syncing bookmarks, passwords, and more with your Firefox account.
+
+ Learn more
@@ -1450,6 +1484,8 @@
View a list of tabs from your other devices.Sign in to sync
+
+ No open tabs
@@ -1458,5 +1494,18 @@
To add a new top site, remove one. Long press the site and select remove.OK, Got It
+
+ Show most visited sites
+
+
+ Remove
+
+ Get the most out of %s.
+
+
+ Collect the things that matter to you
+
+ Group together similar searches, sites, and tabs for quick access later.
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index d2421212f..cc7d78fb7 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -27,6 +27,7 @@
tools:ignore="UnusedResources">@color/destructive_normal_theme
@color/destructive_normal_theme
+ @style/SelectLoginHeaderTextStyle@color/accent_high_contrast_normal_theme
@@ -79,8 +80,10 @@
@color/synced_tabs_separator@color/search_suggestion_indicator_icon_color_normal_theme@color/search_suggestion_indicator_icon_bookmark_color_normal_theme
-
+ @color/select_login_header_normal_theme
+ @color/primary_text_normal_theme
+ @color/caption_text_normal_theme@color/mozac_widget_favicon_background_normal_theme@color/mozac_widget_favicon_border_normal_theme
@@ -124,6 +127,11 @@
@style/DialogButtonStyleLight
+
+
-
diff --git a/app/src/main/res/xml/account_settings_preferences.xml b/app/src/main/res/xml/account_settings_preferences.xml
index bdf69b790..0e409fbc4 100644
--- a/app/src/main/res/xml/account_settings_preferences.xml
+++ b/app/src/main/res/xml/account_settings_preferences.xml
@@ -43,7 +43,6 @@
diff --git a/app/src/main/res/xml/customization_preferences.xml b/app/src/main/res/xml/customization_preferences.xml
index 23434807b..004f81d13 100644
--- a/app/src/main/res/xml/customization_preferences.xml
+++ b/app/src/main/res/xml/customization_preferences.xml
@@ -5,10 +5,10 @@
+ app:iconSpaceReserved="false">
+ app:iconSpaceReserved="false">
@@ -49,4 +49,16 @@
android:summary="@string/preferences_strip_url_description"
android:title="@string/preferences_strip_url_title" />
+
+
+
+
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index f3bf4c39b..c161e4c96 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -109,6 +109,11 @@
android:key="@string/pref_key_delete_browsing_data_on_quit_preference"
android:title="@string/preferences_delete_browsing_data_on_quit" />
+
+
+
+
diff --git a/app/src/migration/java/org/mozilla/fenix/MigratingFenixApplication.kt b/app/src/migration/java/org/mozilla/fenix/MigratingFenixApplication.kt
index c903f5d3e..b6bfec281 100644
--- a/app/src/migration/java/org/mozilla/fenix/MigratingFenixApplication.kt
+++ b/app/src/migration/java/org/mozilla/fenix/MigratingFenixApplication.kt
@@ -28,7 +28,7 @@ class MigratingFenixApplication : FenixApplication() {
.migrateHistory(this.components.core.lazyHistoryStorage)
.migrateBookmarks(
this.components.core.lazyBookmarksStorage,
- this.components.core.topSiteStorage.storage
+ this.components.core.pinnedSiteStorage
)
.migrateLogins(this.components.core.lazyPasswordsStorage)
.migrateFxa(lazy { this.components.backgroundServices.accountManager })
diff --git a/app/src/test/java/org/mozilla/fenix/components/ReviewPromptControllerTest.kt b/app/src/test/java/org/mozilla/fenix/components/ReviewPromptControllerTest.kt
new file mode 100644
index 000000000..53e62f2f0
--- /dev/null
+++ b/app/src/test/java/org/mozilla/fenix/components/ReviewPromptControllerTest.kt
@@ -0,0 +1,207 @@
+/* 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.components
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runBlockingTest
+import mozilla.components.support.test.robolectric.testContext
+import org.junit.Test
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Assert.assertFalse
+import org.junit.runner.RunWith
+import org.mozilla.fenix.HomeActivity
+import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
+
+class TestReviewSettings(
+ override var numberOfAppLaunches: Int = 0,
+ var isDefault: Boolean = false,
+ override var lastReviewPromptTimeInMillis: Long = 0
+) : ReviewSettings {
+ override val isDefaultBrowser: Boolean
+ get() = isDefault
+}
+
+@ExperimentalCoroutinesApi
+@RunWith(FenixRobolectricTestRunner::class)
+class ReviewPromptControllerTest {
+ @Test
+ fun promptReviewDoesNotSetMillis() = runBlockingTest {
+ var promptWasCalled = false
+ val settings = TestReviewSettings(
+ numberOfAppLaunches = 5,
+ isDefault = false,
+ lastReviewPromptTimeInMillis = 0L
+ )
+
+ val controller = ReviewPromptController(
+ testContext,
+ settings,
+ { 100L },
+ { promptWasCalled = true }
+ )
+
+ controller.reviewPromptIsReady = true
+ controller.promptReview(HomeActivity())
+
+ assertEquals(settings.lastReviewPromptTimeInMillis, 0L)
+ assertFalse(promptWasCalled)
+ }
+
+ @Test
+ fun promptReviewSetsMillisIfSuccessful() = runBlockingTest {
+ var promptWasCalled = false
+ val settings = TestReviewSettings(
+ numberOfAppLaunches = 5,
+ isDefault = true,
+ lastReviewPromptTimeInMillis = 0L
+ )
+
+ val controller = ReviewPromptController(
+ testContext,
+ settings,
+ { 100L },
+ { promptWasCalled = true }
+ )
+
+ controller.reviewPromptIsReady = true
+ controller.promptReview(HomeActivity())
+ assertEquals(100L, settings.lastReviewPromptTimeInMillis)
+ assertTrue(promptWasCalled)
+ }
+
+ @Test
+ fun promptReviewWillNotBeCalledIfNotReady() = runBlockingTest {
+ var promptWasCalled = false
+ val settings = TestReviewSettings(
+ numberOfAppLaunches = 5,
+ isDefault = true,
+ lastReviewPromptTimeInMillis = 0L
+ )
+
+ val controller = ReviewPromptController(
+ testContext,
+ settings,
+ { 100L },
+ { promptWasCalled = true }
+ )
+
+ controller.promptReview(HomeActivity())
+ assertFalse(promptWasCalled)
+ }
+
+ @Test
+ fun promptReviewWillUnreadyPromptAfterCalled() = runBlockingTest {
+ var promptWasCalled = false
+ val settings = TestReviewSettings(
+ numberOfAppLaunches = 5,
+ isDefault = true,
+ lastReviewPromptTimeInMillis = 0L
+ )
+
+ val controller = ReviewPromptController(
+ testContext,
+ settings,
+ { 100L },
+ { promptWasCalled = true }
+ )
+
+ controller.reviewPromptIsReady = true
+
+ assertTrue(controller.reviewPromptIsReady)
+ controller.promptReview(HomeActivity())
+
+ assertFalse(controller.reviewPromptIsReady)
+ assertTrue(promptWasCalled)
+ }
+
+ @Test
+ fun trackApplicationLaunch() {
+ val settings = TestReviewSettings(
+ numberOfAppLaunches = 4,
+ isDefault = true,
+ lastReviewPromptTimeInMillis = 0L
+ )
+
+ val controller = ReviewPromptController(
+ testContext,
+ settings,
+ { 0L }
+ )
+
+ assertFalse(controller.reviewPromptIsReady)
+ assertEquals(4, settings.numberOfAppLaunches)
+
+ controller.trackApplicationLaunch()
+
+ assertEquals(5, settings.numberOfAppLaunches)
+ assertTrue(controller.reviewPromptIsReady)
+ }
+
+ @Test
+ fun shouldShowPrompt() {
+ val settings = TestReviewSettings(
+ numberOfAppLaunches = 5,
+ isDefault = true,
+ lastReviewPromptTimeInMillis = 0L
+ )
+
+ val controller = ReviewPromptController(
+ testContext,
+ settings,
+ { TEST_TIME_NOW }
+ )
+
+ // Test first success criteria
+ controller.reviewPromptIsReady = true
+ assertTrue(controller.shouldShowPrompt())
+
+ // Test with last prompt approx 4 months earlier
+ settings.apply {
+ numberOfAppLaunches = 5
+ isDefault = true
+ lastReviewPromptTimeInMillis = MORE_THAN_4_MONTHS_FROM_TEST_TIME_NOW
+ }
+
+ controller.reviewPromptIsReady = true
+ assertTrue(controller.shouldShowPrompt())
+
+ // Test without being the default browser
+ settings.apply {
+ numberOfAppLaunches = 5
+ isDefault = false
+ lastReviewPromptTimeInMillis = 0L
+ }
+
+ controller.reviewPromptIsReady = true
+ assertFalse(controller.shouldShowPrompt())
+
+ // Test with number of app launches < 5
+ settings.apply {
+ numberOfAppLaunches = 4
+ isDefault = true
+ lastReviewPromptTimeInMillis = 0L
+ }
+
+ controller.reviewPromptIsReady = true
+ assertFalse(controller.shouldShowPrompt())
+
+ // Test with last prompt less than 4 months ago
+ settings.apply {
+ numberOfAppLaunches = 5
+ isDefault = true
+ lastReviewPromptTimeInMillis = LESS_THAN_4_MONTHS_FROM_TEST_TIME_NOW
+ }
+
+ controller.reviewPromptIsReady = true
+ assertFalse(controller.shouldShowPrompt())
+ }
+
+ companion object {
+ private const val TEST_TIME_NOW = 1598416882805L
+ private const val MORE_THAN_4_MONTHS_FROM_TEST_TIME_NOW = 1588048882804L
+ private const val LESS_THAN_4_MONTHS_FROM_TEST_TIME_NOW = 1595824882905L
+ }
+}
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 96c5bb86f..5a67c7edc 100644
--- a/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt
+++ b/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt
@@ -14,7 +14,7 @@ class TestComponents(private val context: Context) : Components(context) {
mockk(relaxed = true)
}
override val services by lazy { Services(context, backgroundServices.accountManager) }
- override val core by lazy { TestCore(context) }
+ override val core by lazy { TestCore(context, analytics.crashReporter) }
override val search by lazy { Search(context) }
override val useCases by lazy {
UseCases(
@@ -23,7 +23,8 @@ class TestComponents(private val context: Context) : Components(context) {
core.sessionManager,
core.store,
search.searchEngineManager,
- core.webAppShortcutManager
+ core.webAppShortcutManager,
+ core.topSiteStorage
)
}
override val intentProcessors by lazy { mockk(relaxed = true) }
diff --git a/app/src/test/java/org/mozilla/fenix/components/TestCore.kt b/app/src/test/java/org/mozilla/fenix/components/TestCore.kt
index cfe3ba62d..23c61d2db 100644
--- a/app/src/test/java/org/mozilla/fenix/components/TestCore.kt
+++ b/app/src/test/java/org/mozilla/fenix/components/TestCore.kt
@@ -14,8 +14,10 @@ import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.Settings
import mozilla.components.concept.fetch.Client
import mozilla.components.feature.pwa.WebAppShortcutManager
+import mozilla.components.feature.top.sites.DefaultTopSitesStorage
+import mozilla.components.support.base.crash.CrashReporting
-class TestCore(context: Context) : Core(context) {
+class TestCore(context: Context, crashReporter: CrashReporting) : Core(context, crashReporter) {
override val engine = mockk(relaxed = true) {
every { this@mockk getProperty "settings" } returns mockk(relaxed = true)
@@ -25,4 +27,5 @@ class TestCore(context: Context) : Core(context) {
override val client = mockk()
override val webAppShortcutManager = mockk()
override val thumbnailStorage = mockk()
+ override val topSiteStorage = mockk()
}
diff --git a/app/src/test/java/org/mozilla/fenix/components/metrics/AppAllSourceStartTelemetryTest.kt b/app/src/test/java/org/mozilla/fenix/components/metrics/AppAllSourceStartTelemetryTest.kt
deleted file mode 100644
index f411ae029..000000000
--- a/app/src/test/java/org/mozilla/fenix/components/metrics/AppAllSourceStartTelemetryTest.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.fenix.components.metrics
-
-import android.content.Intent
-import io.mockk.MockKAnnotations
-import io.mockk.every
-import io.mockk.impl.annotations.RelaxedMockK
-import io.mockk.verify
-import mozilla.components.support.utils.toSafeIntent
-import org.junit.Before
-import org.junit.Test
-
-class AppAllSourceStartTelemetryTest {
-
- @RelaxedMockK
- private lateinit var metricController: MetricController
-
- @RelaxedMockK
- private lateinit var intent: Intent
-
- private lateinit var appAllSourceStartTelemetry: AppAllSourceStartTelemetry
-
- @Before
- fun setup() {
- MockKAnnotations.init(this)
- appAllSourceStartTelemetry = AppAllSourceStartTelemetry(metricController)
- }
-
- @Test
- fun `WHEN a main launcher intent is received in HomeActivity THEN an app start metric is recorded from app_icon`() {
-
- every { intent.action } returns Intent.ACTION_MAIN
- every { intent.categories.contains(Intent.CATEGORY_LAUNCHER) } returns true
-
- appAllSourceStartTelemetry.receivedIntentInHomeActivity(intent.toSafeIntent())
-
- val validSource = Event.AppOpenedAllSourceStartup.Source.APP_ICON
- verify(exactly = 1) { metricController.track(Event.AppOpenedAllSourceStartup(validSource)) }
- }
-
- @Test
- fun `WHEN a VIEW intent is received in HomeActivity THEN an app start metric is recorded from link`() {
- every { intent.action } returns Intent.ACTION_VIEW
-
- appAllSourceStartTelemetry.receivedIntentInHomeActivity(intent.toSafeIntent())
-
- val validSource = Event.AppOpenedAllSourceStartup.Source.LINK
- verify(exactly = 1) { metricController.track(Event.AppOpenedAllSourceStartup(validSource)) }
- }
-
- @Test
- fun `WHEN a intent is received in ExternalAppBrowserActivity THEN an app start metric is recorded from custom_tab`() {
- val intent = Intent()
-
- appAllSourceStartTelemetry.receivedIntentInExternalAppBrowserActivity(intent.toSafeIntent())
-
- val validSource = Event.AppOpenedAllSourceStartup.Source.CUSTOM_TAB
- verify(exactly = 1) { metricController.track(Event.AppOpenedAllSourceStartup(validSource)) }
- }
-
- @Test
- fun `GIVEN an app is in the foreground WHEN an intent is received THEN no startup metric is recorded`() {
- appAllSourceStartTelemetry.receivedIntentInHomeActivity(intent.toSafeIntent())
-
- appAllSourceStartTelemetry.receivedIntentInHomeActivity(intent.toSafeIntent())
-
- verify(exactly = 1) { metricController.track(any()) }
- }
-
- @Test
- fun `WHEN application goes in background and comes foreground, THEN an app start metric is recorded`() {
- // first startup
- appAllSourceStartTelemetry.receivedIntentInHomeActivity(intent.toSafeIntent())
-
- // mock application going in the background
- appAllSourceStartTelemetry.onApplicationOnStop()
-
- appAllSourceStartTelemetry.receivedIntentInHomeActivity(intent.toSafeIntent())
-
- verify(exactly = 2) { metricController.track(any()) }
- }
-
- @Test
- fun `WHEN an intent received in HomeActivity is not launcher or does not have VIEW action, THEN an app start is recorded from unknown`() {
- every { intent.action } returns Intent.ACTION_MAIN
-
- appAllSourceStartTelemetry.receivedIntentInHomeActivity(intent.toSafeIntent())
-
- val validSource = Event.AppOpenedAllSourceStartup.Source.UNKNOWN
- verify(exactly = 1) { metricController.track(Event.AppOpenedAllSourceStartup(validSource)) }
- }
-}
diff --git a/app/src/test/java/org/mozilla/fenix/components/metrics/AppStartupTelemetryTest.kt b/app/src/test/java/org/mozilla/fenix/components/metrics/AppStartupTelemetryTest.kt
new file mode 100644
index 000000000..540fa7402
--- /dev/null
+++ b/app/src/test/java/org/mozilla/fenix/components/metrics/AppStartupTelemetryTest.kt
@@ -0,0 +1,266 @@
+/* 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.components.metrics
+
+import android.content.Intent
+import io.mockk.MockKAnnotations
+import io.mockk.clearMocks
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.verify
+import mozilla.components.support.utils.toSafeIntent
+import org.junit.Before
+import org.junit.Test
+import org.junit.Assert.assertTrue
+import org.mozilla.fenix.GleanMetrics.Events
+import org.mozilla.fenix.GleanMetrics.Events.appOpenedAllStartupKeys.hasSavedInstanceState
+import org.mozilla.fenix.GleanMetrics.Events.appOpenedAllStartupKeys.source
+import org.mozilla.fenix.GleanMetrics.Events.appOpenedAllStartupKeys.type
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.APP_ICON
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.UNKNOWN
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.CUSTOM_TAB
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.LINK
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.COLD
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.WARM
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.HOT
+import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.ERROR
+
+class AppStartupTelemetryTest {
+
+ @MockK
+ private lateinit var metricController: MetricController
+ @MockK
+ private lateinit var intent: Intent
+
+ private lateinit var appStartupTelemetry: AppStartupTelemetry
+
+ @Before
+ fun setup() {
+ MockKAnnotations.init(this)
+ appStartupTelemetry = AppStartupTelemetry(metricController)
+ every { metricController.track(any()) } returns Unit
+ }
+
+ @Test
+ fun `WHEN application is launch for the first time through application icon THEN records the correct values`() {
+ setupIntentMock(APP_ICON)
+
+ appStartupTelemetry.onFenixApplicationOnCreate()
+ appStartupTelemetry.onHomeActivityOnCreate(intent.toSafeIntent(), false)
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(APP_ICON, COLD, false)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `WHEN application is launch for the first time through a url link THEN records the correct values`() {
+ setupIntentMock(LINK)
+
+ appStartupTelemetry.onFenixApplicationOnCreate()
+ appStartupTelemetry.onHomeActivityOnCreate(intent.toSafeIntent(), false)
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(LINK, COLD, false)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `WHEN application is launch for the first time through an custom tab THEN records the correct values`() {
+ setupIntentMock(CUSTOM_TAB)
+
+ appStartupTelemetry.onFenixApplicationOnCreate()
+ appStartupTelemetry.onExternalAppBrowserOnCreate(intent.toSafeIntent(), false)
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(CUSTOM_TAB, COLD, false)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `GIVEN, application exists and is backgrounded, WHEN application is launched again through app icon and HomeActivity is recreated THEN records the correct values`() {
+ setupIntentMock(APP_ICON)
+ launchApplicationAndPutApplicationInBackground()
+
+ appStartupTelemetry.onHomeActivityOnCreate(intent.toSafeIntent(), false)
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(APP_ICON, WARM, false)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `GIVEN, application exists and is backgrounded, WHEN application is launched again through url link and HomeActivity is recreated THEN records the correct values`() {
+ setupIntentMock(LINK)
+ launchApplicationAndPutApplicationInBackground()
+
+ appStartupTelemetry.onHomeActivityOnCreate(intent.toSafeIntent(), false)
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(LINK, WARM, false)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `GIVEN, application exists and is backgrounded, WHEN application is launched again through custom tab and ExternalAppBrowserActivity is recreated THEN records the correct values`() {
+ setupIntentMock(CUSTOM_TAB)
+ launchApplicationAndPutApplicationInBackground()
+
+ appStartupTelemetry.onExternalAppBrowserOnCreate(intent.toSafeIntent(), false)
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(CUSTOM_TAB, WARM, false)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `GIVEN, application exists and is backgrounded, WHEN application is launched again through app icon and HomeActivity is restarted THEN records the correct values`() {
+ setupIntentMock(APP_ICON)
+ launchApplicationAndPutApplicationInBackground()
+
+ appStartupTelemetry.onHomeActivityOnRestart()
+ appStartupTelemetry.onHomeActivityOnNewIntent(intent.toSafeIntent())
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(APP_ICON, HOT)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `GIVEN, application exists and is backgrounded, WHEN application is launched again through url link and HomeActivity is restarted THEN records the correct values`() {
+ setupIntentMock(LINK)
+ launchApplicationAndPutApplicationInBackground()
+
+ appStartupTelemetry.onHomeActivityOnRestart()
+ appStartupTelemetry.onHomeActivityOnNewIntent(intent.toSafeIntent())
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(LINK, HOT)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `WHEN application is launched and onResume() is called twice THEN metric is reported only once`() {
+ setupIntentMock(LINK)
+ appStartupTelemetry.onExternalAppBrowserOnCreate(intent.toSafeIntent(), false)
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ verify(exactly = 1) { metricController.track(any()) }
+ }
+
+ @Test
+ fun `GIVEN application is in background WHEN application is launched again through unknown source and HomeActivity exists THEN records the correct values`() {
+ setupIntentMock(UNKNOWN)
+ launchApplicationAndPutApplicationInBackground()
+
+ appStartupTelemetry.onHomeActivityOnCreate(intent.toSafeIntent(), false)
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(UNKNOWN, WARM, false)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `GIVEN application exists and is backgrounded WHEN application started again through app icon but HomeActivity is recreated from savedInstanceState THEN records the correct values`() {
+ setupIntentMock(APP_ICON)
+ launchApplicationAndPutApplicationInBackground()
+
+ appStartupTelemetry.onHomeActivityOnCreate(intent.toSafeIntent(), true)
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(APP_ICON, WARM, true)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ private fun launchApplicationAndPutApplicationInBackground() {
+ appStartupTelemetry.onFenixApplicationOnCreate()
+ appStartupTelemetry.onHomeActivityOnCreate(intent.toSafeIntent(), false)
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ // have to clear the mock function calls so it doesnt interfere with tests
+ clearMocks(metricController, answers = false)
+
+ appStartupTelemetry.onApplicationOnStop()
+ }
+
+ @Test
+ fun `GIVEN application is in background WHEN application is launched again HomeActivity only calls onResume THEN records the correct values`() {
+ setupIntentMock(UNKNOWN)
+ launchApplicationAndPutApplicationInBackground()
+
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(UNKNOWN, ERROR)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `GIVEN application is in background WHEN application is launched again HomeActivity calls onRestart but not onNewIntent THEN records the correct values`() {
+ setupIntentMock(APP_ICON)
+ launchApplicationAndPutApplicationInBackground()
+
+ appStartupTelemetry.onHomeActivityOnRestart()
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(UNKNOWN, HOT)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `GIVEN application is in background WHEN application is launched again and HomeActivity calls onNewIntent but not onRestart THEN records the correct values`() {
+ setupIntentMock(APP_ICON)
+ launchApplicationAndPutApplicationInBackground()
+
+ appStartupTelemetry.onHomeActivityOnNewIntent(intent.toSafeIntent())
+ appStartupTelemetry.onHomeActivityOnResume()
+
+ val validMetric = AppAllStartup(APP_ICON, ERROR)
+ verify(exactly = 1) { metricController.track(validMetric) }
+ }
+
+ @Test
+ fun `WHEN AppAllStartup does not have savedInstanceState THEN do not return savedInstanceState`() {
+ val expectedExtra: Map? = hashMapOf(
+ source to APP_ICON.toString(),
+ type to HOT.toString())
+
+ val appAllStartup = AppAllStartup(APP_ICON, HOT)
+
+ assertTrue(appAllStartup.extras!! == expectedExtra)
+ }
+
+ @Test
+ fun `WHEN AppAllStartup have savedInstanceState THEN return savedInstanceState `() {
+ val expectedExtra: Map? = hashMapOf(
+ source to APP_ICON.toString(),
+ type to COLD.toString(),
+ hasSavedInstanceState to true.toString())
+
+ val appAllStartup = AppAllStartup(APP_ICON, COLD, true)
+
+ assertTrue(appAllStartup.extras!! == expectedExtra)
+ }
+
+ private fun setupIntentMock(source: Source) {
+ when (source) {
+ APP_ICON -> {
+ every { intent.action } returns Intent.ACTION_MAIN
+ every { intent.categories } returns setOf(Intent.CATEGORY_LAUNCHER)
+ }
+ LINK, CUSTOM_TAB -> {
+ every { intent.action } returns Intent.ACTION_VIEW
+ every { intent.categories } returns emptySet()
+ }
+ UNKNOWN -> {
+ every { intent.action } returns Intent.ACTION_MAIN
+ every { intent.categories } returns emptySet()
+ }
+ }
+ }
+}
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 196b9cc12..1d38f6a9d 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
@@ -38,6 +38,7 @@ import mozilla.components.feature.session.SessionFeature
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.tabs.TabsUseCases
+import mozilla.components.feature.top.sites.TopSitesUseCases
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import mozilla.components.support.test.rule.MainCoroutineRule
import org.junit.After
@@ -59,7 +60,6 @@ import org.mozilla.fenix.collections.SaveCollectionStep
import org.mozilla.fenix.components.Analytics
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.TabCollectionStorage
-import org.mozilla.fenix.components.TopSiteStorage
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.ext.components
@@ -93,7 +93,7 @@ class DefaultBrowserToolbarControllerTest {
@RelaxedMockK private lateinit var browserAnimator: BrowserAnimator
@RelaxedMockK private lateinit var snackbar: FenixSnackbar
@RelaxedMockK private lateinit var tabCollectionStorage: TabCollectionStorage
- @RelaxedMockK private lateinit var topSiteStorage: TopSiteStorage
+ @RelaxedMockK private lateinit var topSitesUseCase: TopSitesUseCases
@RelaxedMockK private lateinit var readerModeController: ReaderModeController
@RelaxedMockK private lateinit var sessionFeatureWrapper: ViewBoundFeatureWrapper
@RelaxedMockK private lateinit var sessionFeature: SessionFeature
@@ -120,6 +120,7 @@ class DefaultBrowserToolbarControllerTest {
every { analytics.metrics } returns metrics
every { activity.components.useCases.sessionUseCases } returns sessionUseCases
every { activity.components.useCases.searchUseCases } returns searchUseCases
+ every { activity.components.useCases.topSitesUseCase } returns topSitesUseCase
every { activity.components.core.sessionManager } returns sessionManager
every { activity.components.core.store } returns store
every { sessionManager.selectedSession } returns currentSession
@@ -397,6 +398,9 @@ class DefaultBrowserToolbarControllerTest {
@Test
fun handleToolbarAddToTopSitesPressed() = runBlockingTest {
val item = ToolbarMenu.Item.AddToTopSites
+ val addPinnedSiteUseCase: TopSitesUseCases.AddPinnedSiteUseCase = mockk(relaxed = true)
+
+ every { topSitesUseCase.addPinnedSites } returns addPinnedSiteUseCase
every {
swipeRefreshLayout.context.getString(R.string.snackbar_added_to_top_sites)
} returns "Added to top sites!"
@@ -404,7 +408,7 @@ class DefaultBrowserToolbarControllerTest {
val controller = createController(scope = this)
controller.handleToolbarItemInteraction(item)
- verify { topSiteStorage.addTopSite(currentSession.title, currentSession.url) }
+ verify { addPinnedSiteUseCase.invoke(currentSession.title, currentSession.url) }
verify { snackbar.setText("Added to top sites!") }
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.ADD_TO_TOP_SITES)) }
}
@@ -636,7 +640,6 @@ class DefaultBrowserToolbarControllerTest {
scope = scope,
swipeRefresh = swipeRefreshLayout,
tabCollectionStorage = tabCollectionStorage,
- topSiteStorage = topSiteStorage,
bookmarkTapped = bookmarkTapped,
readerModeController = readerModeController,
sessionManager = sessionManager,
diff --git a/app/src/test/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsListItemViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsListItemViewHolderTest.kt
index e0cc1ed94..2779de9a5 100644
--- a/app/src/test/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsListItemViewHolderTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsListItemViewHolderTest.kt
@@ -5,9 +5,6 @@
package org.mozilla.fenix.exceptions.viewholders
import android.view.View
-import android.widget.ImageButton
-import android.widget.ImageView
-import android.widget.TextView
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.every
@@ -18,17 +15,14 @@ import io.mockk.slot
import io.mockk.verify
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.icons.IconRequest
+import mozilla.components.ui.widgets.WidgetSiteItemView
import org.junit.Before
import org.junit.Test
-import org.mozilla.fenix.R
import org.mozilla.fenix.exceptions.ExceptionsInteractor
class ExceptionsListItemViewHolderTest {
- @MockK private lateinit var view: View
- @MockK(relaxUnitFun = true) private lateinit var url: TextView
- @MockK(relaxUnitFun = true) private lateinit var deleteButton: ImageButton
- @MockK private lateinit var favicon: ImageView
+ @MockK(relaxed = true) private lateinit var view: WidgetSiteItemView
@MockK private lateinit var icons: BrowserIcons
@MockK private lateinit var interactor: ExceptionsInteractor
@@ -36,37 +30,34 @@ class ExceptionsListItemViewHolderTest {
fun setup() {
MockKAnnotations.init(this)
- every { view.findViewById(R.id.webAddressView) } returns url
- every { view.findViewById(R.id.delete_exception) } returns deleteButton
- every { view.findViewById(R.id.favicon_image) } returns favicon
- every { icons.loadIntoView(favicon, any()) } returns mockk()
+ every { icons.loadIntoView(view.iconView, any()) } returns mockk()
}
@Test
fun `sets url text and loads favicon - mozilla`() {
ExceptionsListItemViewHolder(view, interactor, icons)
.bind(Exception(), url = "mozilla.org")
- verify { url.text = "mozilla.org" }
- verify { icons.loadIntoView(favicon, IconRequest("mozilla.org")) }
+ verify { view.setText(label = "mozilla.org", caption = null) }
+ verify { icons.loadIntoView(view.iconView, IconRequest("mozilla.org")) }
}
@Test
fun `sets url text and loads favicon - example`() {
ExceptionsListItemViewHolder(view, interactor, icons)
.bind(Exception(), url = "https://example.com/icon.svg")
- verify { url.text = "https://example.com/icon.svg" }
- verify { icons.loadIntoView(favicon, IconRequest("https://example.com/icon.svg")) }
+ verify { view.setText(label = "https://example.com/icon.svg", caption = null) }
+ verify { icons.loadIntoView(view.iconView, IconRequest("https://example.com/icon.svg")) }
}
@Test
fun `delete button calls interactor`() {
- val slot = slot()
+ val slot = slot<(View) -> Unit>()
val exception = Exception()
- every { deleteButton.setOnClickListener(capture(slot)) } just Runs
+ every { view.setSecondaryButton(any(), any(), capture(slot)) } just Runs
ExceptionsListItemViewHolder(view, interactor, icons).bind(exception, url = "mozilla.org")
every { interactor.onDeleteOne(exception) } just Runs
- slot.captured.onClick(mockk())
+ slot.captured.invoke(mockk())
verify { interactor.onDeleteOne(exception) }
}
diff --git a/app/src/test/java/org/mozilla/fenix/ext/DownloadItemKtTest.kt b/app/src/test/java/org/mozilla/fenix/ext/DownloadItemKtTest.kt
new file mode 100644
index 000000000..d4881dddb
--- /dev/null
+++ b/app/src/test/java/org/mozilla/fenix/ext/DownloadItemKtTest.kt
@@ -0,0 +1,28 @@
+/* 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 mozilla.components.browser.state.state.content.DownloadState
+import org.junit.Test
+
+import org.junit.Assert.assertEquals
+import org.mozilla.fenix.R
+import org.mozilla.fenix.library.downloads.DownloadItem
+
+class DownloadItemKtTest {
+ @Test
+ fun getIcon() {
+ val downloadItem = DownloadItem("0", "MyAwesomeFile", "", "", "image/png", DownloadState.Status.COMPLETED)
+
+ assertEquals(R.drawable.ic_file_type_image, downloadItem.getIcon())
+ assertEquals(R.drawable.ic_file_type_audio_note, downloadItem.copy(contentType = "audio/mp3").getIcon())
+ assertEquals(R.drawable.ic_file_type_video, downloadItem.copy(contentType = "video/mp4").getIcon())
+ assertEquals(R.drawable.ic_file_type_document, downloadItem.copy(contentType = "text/csv").getIcon())
+ assertEquals(R.drawable.ic_file_type_zip, downloadItem.copy(contentType = "application/gzip").getIcon())
+ assertEquals(R.drawable.ic_file_type_apk, downloadItem.copy(contentType = null, fileName = "Fenix.apk").getIcon())
+ assertEquals(R.drawable.ic_file_type_zip, downloadItem.copy(contentType = null, fileName = "Fenix.zip").getIcon())
+ assertEquals(R.drawable.ic_file_type_default, downloadItem.copy(contentType = null, fileName = null).getIcon())
+ }
+}
diff --git a/app/src/test/java/org/mozilla/fenix/ext/ListTest.kt b/app/src/test/java/org/mozilla/fenix/ext/ListTest.kt
new file mode 100644
index 000000000..e6ff4a337
--- /dev/null
+++ b/app/src/test/java/org/mozilla/fenix/ext/ListTest.kt
@@ -0,0 +1,77 @@
+/* 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 mozilla.components.browser.state.state.content.DownloadState
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
+import org.mozilla.fenix.library.downloads.DownloadItem
+import java.io.File
+
+@RunWith(FenixRobolectricTestRunner::class)
+class ListTest {
+
+ @Test
+ fun `Test download in list but not on disk removed from list`() {
+ val filePath1 = "filepath.txt"
+ val filePath3 = "filepath3.txt"
+
+ var file1 = File(filePath1)
+ var file3 = File(filePath3)
+
+ // Create files
+ file1.createNewFile()
+ file3.createNewFile()
+
+ val item1 = DownloadItem("71", "filepath.txt", filePath1, "71 Mb", "Image/png", DownloadState.Status.COMPLETED)
+ val item2 = DownloadItem("71", "filepath2.txt", "filepath2.txt", "71 Mb", "Image/png", DownloadState.Status.COMPLETED)
+ val item3 = DownloadItem("71", "filepath3.txt", filePath3, "71 Mb", "Image/png", DownloadState.Status.COMPLETED)
+
+ val testList = mutableListOf(item1, item2, item3)
+ val comparisonList: MutableList = mutableListOf(item1, item3)
+
+ val resultList = testList.filterNotExistsOnDisk()
+
+ assertEquals(comparisonList, resultList)
+
+ // Cleanup files
+ file1.delete()
+ file3.delete()
+ }
+
+ @Test
+ fun `Test download in list and on disk remain in list`() {
+ val filePath1 = "filepath.txt"
+ val filePath2 = "filepath.txt"
+ val filePath3 = "filepath3.txt"
+
+ var file1 = File(filePath1)
+ var file2 = File(filePath2)
+ var file3 = File(filePath3)
+
+ // Create files
+ file1.createNewFile()
+ file2.createNewFile()
+ file3.createNewFile()
+
+ val item1 = DownloadItem("71", "filepath.txt", filePath1, "71 Mb", "text/plain", DownloadState.Status.COMPLETED)
+ val item2 = DownloadItem("72", "filepath2.txt", filePath2, "71 Mb", "text/plain", DownloadState.Status.COMPLETED)
+ val item3 = DownloadItem("73", "filepath3.txt", filePath3, "71 Mb", "text/plain", DownloadState.Status.COMPLETED)
+
+ val testList = mutableListOf(item1, item2, item3)
+ val comparisonList: MutableList = mutableListOf(item1, item2, item3)
+
+ val resultList = testList.filterNotExistsOnDisk()
+
+ assertEquals(comparisonList, resultList)
+
+ // Cleanup files
+ file1.delete()
+ file2.delete()
+ file3.delete()
+ }
+}
diff --git a/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt b/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt
index bf63fcce6..d114f5002 100644
--- a/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt
@@ -27,7 +27,6 @@ import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.Analytics
import org.mozilla.fenix.components.TabCollectionStorage
-import org.mozilla.fenix.components.TopSiteStorage
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.components.tips.Tip
@@ -52,7 +51,6 @@ class DefaultSessionControlControllerTest {
private val sessionManager: SessionManager = mockk(relaxed = true)
private val engine: Engine = mockk(relaxed = true)
private val tabCollectionStorage: TabCollectionStorage = mockk(relaxed = true)
- private val topSiteStorage: TopSiteStorage = mockk(relaxed = true)
private val tabsUseCases: TabsUseCases = mockk(relaxed = true)
private val hideOnboarding: () -> Unit = mockk(relaxed = true)
private val registerCollectionStorageObserver: () -> Unit = mockk(relaxed = true)
@@ -78,8 +76,10 @@ class DefaultSessionControlControllerTest {
collections = emptyList(),
expandedCollections = emptySet(),
mode = Mode.Normal,
- topSites = emptyList()
+ topSites = emptyList(),
+ showCollectionPlaceholder = true
)
+
every { sessionManager.sessions } returns emptyList()
every { navController.currentDestination } returns mockk {
every { id } returns R.id.homeFragment
@@ -94,11 +94,11 @@ class DefaultSessionControlControllerTest {
controller = DefaultSessionControlController(
activity = activity,
+ settings = settings,
engine = engine,
metrics = metrics,
sessionManager = sessionManager,
tabCollectionStorage = tabCollectionStorage,
- topSiteStorage = topSiteStorage,
addTabUseCase = tabsUseCases.addTab,
fragmentStore = fragmentStore,
navController = navController,
@@ -414,4 +414,14 @@ class DefaultSessionControlControllerTest {
)
}
}
+
+ @Test
+ fun handleRemoveCollectionsPlaceholder() {
+ controller.handleRemoveCollectionsPlaceholder()
+
+ verify {
+ settings.showCollectionsPlaceholderOnHome = false
+ fragmentStore.dispatch(HomeFragmentAction.RemoveCollectionsPlaceholder)
+ }
+ }
}
diff --git a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt
index ed217152f..87baabbb7 100644
--- a/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/home/HomeFragmentStoreTest.kt
@@ -12,6 +12,7 @@ import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.top.sites.TopSite
import mozilla.components.service.fxa.manager.FxaAccountManager
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -53,7 +54,8 @@ class HomeFragmentStoreTest {
collections = emptyList(),
expandedCollections = emptySet(),
mode = currentMode.getCurrentMode(),
- topSites = emptyList()
+ topSites = emptyList(),
+ showCollectionPlaceholder = true
)
homeFragmentStore = HomeFragmentStore(homeFragmentState)
@@ -95,6 +97,15 @@ class HomeFragmentStoreTest {
assertEquals(topSites, homeFragmentStore.state.topSites)
}
+ @Test
+ fun `Test changing hiding collections placeholder`() = runBlocking {
+ assertTrue(homeFragmentStore.state.showCollectionPlaceholder)
+
+ homeFragmentStore.dispatch(HomeFragmentAction.RemoveCollectionsPlaceholder).join()
+
+ assertFalse(homeFragmentStore.state.showCollectionPlaceholder)
+ }
+
@Test
fun `Test changing the expanded collections in HomeFragmentStore`() = runBlocking {
val collection: TabCollection = mockk().apply {
@@ -110,25 +121,27 @@ class HomeFragmentStoreTest {
}
@Test
- fun `Test changing the collections, mode and top sites in the HomeFragmentStore`() = runBlocking {
- // Verify that the default state of the HomeFragment is correct.
- assertEquals(0, homeFragmentStore.state.collections.size)
- assertEquals(0, homeFragmentStore.state.topSites.size)
- assertEquals(Mode.Normal, homeFragmentStore.state.mode)
-
- val collections: List = listOf(mockk())
- val topSites: List = listOf(mockk(), mockk())
-
- homeFragmentStore.dispatch(
- HomeFragmentAction.Change(
- collections = collections,
- mode = Mode.Private,
- topSites = topSites
- )
- ).join()
-
- assertEquals(1, homeFragmentStore.state.collections.size)
- assertEquals(Mode.Private, homeFragmentStore.state.mode)
- assertEquals(2, homeFragmentStore.state.topSites.size)
- }
+ fun `Test changing the collections, mode and top sites in the HomeFragmentStore`() =
+ runBlocking {
+ // Verify that the default state of the HomeFragment is correct.
+ assertEquals(0, homeFragmentStore.state.collections.size)
+ assertEquals(0, homeFragmentStore.state.topSites.size)
+ assertEquals(Mode.Normal, homeFragmentStore.state.mode)
+
+ val collections: List = listOf(mockk())
+ val topSites: List = listOf(mockk(), mockk())
+
+ homeFragmentStore.dispatch(
+ HomeFragmentAction.Change(
+ collections = collections,
+ mode = Mode.Private,
+ topSites = topSites,
+ showCollectionPlaceholder = true
+ )
+ ).join()
+
+ assertEquals(1, homeFragmentStore.state.collections.size)
+ assertEquals(Mode.Private, homeFragmentStore.state.mode)
+ assertEquals(2, homeFragmentStore.state.topSites.size)
+ }
}
diff --git a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt
index 63bc74661..6357cf51f 100644
--- a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt
@@ -110,4 +110,10 @@ class SessionControlInteractorTest {
interactor.onPasteAndGo("text")
verify { controller.handlePasteAndGo("text") }
}
+
+ @Test
+ fun onRemoveCollectionsPlaceholder() {
+ interactor.onRemoveCollectionsPlaceholder()
+ verify { controller.handleRemoveCollectionsPlaceholder() }
+ }
}
diff --git a/app/src/test/java/org/mozilla/fenix/home/intent/DeepLinkIntentProcessorTest.kt b/app/src/test/java/org/mozilla/fenix/home/intent/DeepLinkIntentProcessorTest.kt
index 19b959b2e..959200d5e 100644
--- a/app/src/test/java/org/mozilla/fenix/home/intent/DeepLinkIntentProcessorTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/home/intent/DeepLinkIntentProcessorTest.kt
@@ -157,7 +157,7 @@ class DeepLinkIntentProcessorTest {
fun `process settings_addon_manager deep link`() {
assertTrue(processor.process(testIntent("settings_addon_manager"), navController, out))
- verify { navController.navigate(NavGraphDirections.actionGlobalSettingsAddonsManagementFragment()) }
+ verify { navController.navigate(NavGraphDirections.actionGlobalAddonsManagementFragment()) }
verify { out wasNot Called }
}
diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolderTest.kt
index 54b50341c..643a1e6fd 100644
--- a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolderTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolderTest.kt
@@ -8,9 +8,13 @@ import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.view.isVisible
+import androidx.lifecycle.LifecycleOwner
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.no_collections_message.view.*
+import mozilla.components.browser.state.state.BrowserState
+import mozilla.components.browser.state.state.createTab
+import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -25,6 +29,14 @@ import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
class NoCollectionsMessageViewHolderTest {
private lateinit var view: View
+ private val store: BrowserStore = BrowserStore(
+ initialState = BrowserState(
+ listOf(
+ createTab("https://www.mozilla.org", id = "reader-inactive-tab")
+ )
+ )
+ )
+ private lateinit var lifecycleOwner: LifecycleOwner
private lateinit var interactor: CollectionInteractor
@Before
@@ -32,28 +44,40 @@ class NoCollectionsMessageViewHolderTest {
val appCompatContext = ContextThemeWrapper(testContext, R.style.NormalTheme)
view = LayoutInflater.from(appCompatContext)
.inflate(NoCollectionsMessageViewHolder.LAYOUT_ID, null)
+ lifecycleOwner = mockk(relaxed = true)
interactor = mockk(relaxed = true)
}
@Test
- fun `hide button when hasNormalTabsOpened is false`() {
- NoCollectionsMessageViewHolder(view, interactor, hasNormalTabsOpened = false)
+ fun `hide add to collection button when there are no tabs open`() {
+ val noTabsStore = BrowserStore()
+ NoCollectionsMessageViewHolder(view, lifecycleOwner, noTabsStore, interactor)
assertFalse(view.add_tabs_to_collections_button.isVisible)
}
@Test
- fun `show button when hasNormalTabsOpened is true`() {
- NoCollectionsMessageViewHolder(view, interactor, hasNormalTabsOpened = true)
+ fun `show add to collection button when there are tabs`() {
+ NoCollectionsMessageViewHolder(view, lifecycleOwner, store, interactor)
assertTrue(view.add_tabs_to_collections_button.isVisible)
}
@Test
fun `call interactor on click`() {
- NoCollectionsMessageViewHolder(view, interactor, hasNormalTabsOpened = true)
+ NoCollectionsMessageViewHolder(view, lifecycleOwner, store, interactor)
view.add_tabs_to_collections_button.performClick()
verify { interactor.onAddTabsToCollectionTapped() }
}
+
+ @Test
+ fun `hide view and change setting on remove placeholder click`() {
+ NoCollectionsMessageViewHolder(view, lifecycleOwner, store, interactor)
+
+ view.remove_collection_placeholder.performClick()
+ verify {
+ interactor.onRemoveCollectionsPlaceholder()
+ }
+ }
}
diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSiteViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSiteViewHolderTest.kt
index 8477eb7d2..b2350f8c2 100644
--- a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSiteViewHolderTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSiteViewHolderTest.kt
@@ -32,14 +32,17 @@ class TopSiteViewHolderTest {
@Test
fun `binds list of top sites`() {
- TopSiteViewHolder(view, interactor).bind(listOf(
- object : TopSite {
- override val id = 1L
- override val isDefault = true
- override val title = "Pocket"
- override val url = "https://getpocket.com"
- }
- ))
+ TopSiteViewHolder(view, interactor).bind(
+ listOf(
+ TopSite(
+ id = 1L,
+ title = "Pocket",
+ url = "https://getpocket.com",
+ createdAt = 0,
+ type = TopSite.Type.DEFAULT
+ )
+ )
+ )
assertEquals(1, view.top_sites_list.adapter!!.itemCount)
}
diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingAutomaticSignInViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingAutomaticSignInViewHolderTest.kt
index 51464285a..0bd074785 100644
--- a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingAutomaticSignInViewHolderTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingAutomaticSignInViewHolderTest.kt
@@ -65,7 +65,7 @@ class OnboardingAutomaticSignInViewHolderTest {
"You are signed in as email@example.com on another Firefox browser on this phone. Would you like to sign in with this account?",
view.header_text.text
)
- assertTrue(view.turn_on_sync_button.isEnabled)
+ assertTrue(view.fxa_sign_in_button.isEnabled)
}
@Test
@@ -79,10 +79,10 @@ class OnboardingAutomaticSignInViewHolderTest {
val holder = OnboardingAutomaticSignInViewHolder(view, scope = this)
holder.bind(account)
- holder.onClick(view.turn_on_sync_button)
+ holder.onClick(view.fxa_sign_in_button)
- assertEquals("Signing in…", view.turn_on_sync_button.text)
- assertFalse(view.turn_on_sync_button.isEnabled)
+ assertEquals("Signing in…", view.fxa_sign_in_button.text)
+ assertFalse(view.fxa_sign_in_button.isEnabled)
}
@Test
@@ -96,10 +96,10 @@ class OnboardingAutomaticSignInViewHolderTest {
val holder = OnboardingAutomaticSignInViewHolder(view, scope = this)
holder.bind(account)
- holder.onClick(view.turn_on_sync_button)
+ holder.onClick(view.fxa_sign_in_button)
- assertEquals("Yes, sign me in", view.turn_on_sync_button.text)
- assertTrue(view.turn_on_sync_button.isEnabled)
+ assertEquals("Yes, sign me in", view.fxa_sign_in_button.text)
+ assertTrue(view.fxa_sign_in_button.isEnabled)
verify { snackbar.setText("Failed to sign-in") }
}
}
diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolderTest.kt
index 5fc02b4e7..c87091cd8 100644
--- a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolderTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolderTest.kt
@@ -6,9 +6,12 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup
import androidx.navigation.NavController
import androidx.navigation.Navigation
+import io.mockk.Runs
import io.mockk.every
+import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
@@ -22,20 +25,27 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.HomeFragmentDirections
+import org.mozilla.fenix.onboarding.OnboardingInteractor
@RunWith(FenixRobolectricTestRunner::class)
class OnboardingManualSignInViewHolderTest {
private lateinit var view: View
private lateinit var navController: NavController
+ private lateinit var interactor: OnboardingInteractor
+ private lateinit var itemView: ViewGroup
@Before
fun setup() {
view = LayoutInflater.from(testContext)
.inflate(OnboardingManualSignInViewHolder.LAYOUT_ID, null)
navController = mockk(relaxed = true)
+ interactor = mockk(relaxUnitFun = true)
+ itemView = mockk(relaxed = true)
mockkStatic(Navigation::class)
+ every { itemView.context } returns testContext
+ every { interactor.onLearnMoreClicked() } just Runs
every { Navigation.findNavController(view) } returns navController
}
@@ -48,13 +58,16 @@ class OnboardingManualSignInViewHolderTest {
fun `bind header text`() {
OnboardingManualSignInViewHolder(view).bind()
- assertEquals("Get the most out of Firefox Preview.", view.header_text.text)
+ assertEquals(
+ "Start syncing bookmarks, passwords, and more with your Firefox account.",
+ view.header_text.text
+ )
}
@Test
fun `navigate on click`() {
OnboardingManualSignInViewHolder(view)
- view.turn_on_sync_button.performClick()
+ view.fxa_sign_in_button.performClick()
verify { navController.navigate(HomeFragmentDirections.actionGlobalTurnOnSync()) }
}
diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolderTest.kt
index 47c18277c..321bad7a9 100644
--- a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolderTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolderTest.kt
@@ -22,12 +22,13 @@ class TopSiteItemViewHolderTest {
private lateinit var view: View
private lateinit var interactor: TopSiteInteractor
- private val pocket = object : TopSite {
- override val id = 1L
- override val isDefault = true
- override val title = "Pocket"
- override val url = "https://getpocket.com"
- }
+ private val pocket = TopSite(
+ id = 1L,
+ title = "Pocket",
+ url = "https://getpocket.com",
+ createdAt = 0,
+ type = TopSite.Type.DEFAULT
+ )
@Before
fun setup() {
diff --git a/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadAdapterTest.kt b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadAdapterTest.kt
new file mode 100644
index 000000000..79ba1a8ab
--- /dev/null
+++ b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadAdapterTest.kt
@@ -0,0 +1,56 @@
+/* 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.library.downloads
+
+import androidx.recyclerview.widget.RecyclerView
+import io.mockk.Runs
+import io.mockk.every
+import io.mockk.just
+import io.mockk.mockk
+import io.mockk.verify
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
+
+@RunWith(FenixRobolectricTestRunner::class)
+class DownloadAdapterTest {
+
+ private lateinit var interactor: DownloadInteractor
+ private lateinit var adapter: DownloadAdapter
+
+ @Before
+ fun setup() {
+ interactor = mockk()
+ adapter = DownloadAdapter(interactor)
+
+ every { interactor.select(any()) } just Runs
+ }
+
+ @Test
+ fun `getItemCount should return the number of tab collections`() {
+ val download = mockk()
+
+ assertEquals(0, adapter.itemCount)
+
+ adapter.updateDownloads(
+ downloads = listOf(download)
+ )
+ assertEquals(1, adapter.itemCount)
+ }
+
+ @Test
+ fun `updateData inserts item`() {
+ val download = mockk {
+ }
+ val observer = mockk(relaxed = true)
+ adapter.registerAdapterDataObserver(observer)
+ adapter.updateDownloads(
+ downloads = listOf(download)
+ )
+ verify { observer.onChanged() }
+ }
+}
diff --git a/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadControllerTest.kt b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadControllerTest.kt
new file mode 100644
index 000000000..75d3d7418
--- /dev/null
+++ b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadControllerTest.kt
@@ -0,0 +1,64 @@
+/* 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.library.downloads
+
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineScope
+import mozilla.components.browser.state.state.content.DownloadState
+import org.junit.Assert.assertFalse
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mozilla.fenix.browser.browsingmode.BrowsingMode
+import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
+
+@ExperimentalCoroutinesApi
+@RunWith(FenixRobolectricTestRunner::class)
+class DownloadControllerTest {
+ private val downloadItem = DownloadItem("0", "title", "url", "77", "jpg", DownloadState.Status.COMPLETED)
+ private val scope: CoroutineScope = TestCoroutineScope()
+ private val store: DownloadFragmentStore = mockk(relaxed = true)
+ private val state: DownloadFragmentState = mockk(relaxed = true)
+ private val openToFileManager: (DownloadItem, BrowsingMode?) -> Unit = mockk(relaxed = true)
+ private val invalidateOptionsMenu: () -> Unit = mockk(relaxed = true)
+ private val controller = DefaultDownloadController(
+ store,
+ openToFileManager
+ )
+
+ @Before
+ fun setUp() {
+ every { store.state } returns state
+ }
+
+ @Test
+ fun onPressDownloadItemInNormalMode() {
+ controller.handleOpen(downloadItem)
+
+ verify {
+ openToFileManager(downloadItem, null)
+ }
+ }
+
+ @Test
+ fun onOpenItemInNormalMode() {
+ controller.handleOpen(downloadItem, BrowsingMode.Normal)
+
+ verify {
+ openToFileManager(downloadItem, BrowsingMode.Normal)
+ }
+ }
+
+ @Test
+ fun onBackPressedInNormalMode() {
+ every { state.mode } returns DownloadFragmentState.Mode.Normal
+
+ assertFalse(controller.handleBackPressed())
+ }
+}
diff --git a/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadInteractorTest.kt
new file mode 100644
index 000000000..74a80f888
--- /dev/null
+++ b/app/src/test/java/org/mozilla/fenix/library/downloads/DownloadInteractorTest.kt
@@ -0,0 +1,41 @@
+/* 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.library.downloads
+
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verifyAll
+import mozilla.components.browser.state.state.content.DownloadState
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class DownloadInteractorTest {
+ private val downloadItem = DownloadItem("0", "title", "url", "5.6 mb", "png", DownloadState.Status.COMPLETED)
+ val controller: DownloadController = mockk(relaxed = true)
+ val interactor = DownloadInteractor(controller)
+
+ @Test
+ fun onOpen() {
+ interactor.open(downloadItem)
+
+ verifyAll {
+ controller.handleOpen(downloadItem)
+ }
+ }
+
+ @Test
+ fun onBackPressed() {
+ every {
+ controller.handleBackPressed()
+ } returns true
+
+ val backpressHandled = interactor.onBackPressed()
+
+ verifyAll {
+ controller.handleBackPressed()
+ }
+ assertTrue(backpressHandled)
+ }
+}
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 6a8a25c81..d6a223342 100644
--- a/app/src/test/java/org/mozilla/fenix/search/DefaultSearchControllerTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/search/DefaultSearchControllerTest.kt
@@ -29,7 +29,6 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.components.metrics.MetricsUtils
-import org.mozilla.fenix.ext.navigateSafe
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.utils.Settings
@@ -55,6 +54,9 @@ class DefaultSearchControllerTest {
every { store.state.tabId } returns "test-tab-id"
every { store.state.searchEngineSource.searchEngine } returns searchEngine
every { sessionManager.select(any()) } just Runs
+ every { navController.currentDestination } returns mockk {
+ every { id } returns R.id.searchFragment
+ }
every { MetricsUtils.createSearchEvent(searchEngine, activity, any()) } returns null
controller = DefaultSearchController(
@@ -119,6 +121,16 @@ class DefaultSearchControllerTest {
}
}
+ @Test
+ fun handleAddonsUrlCommitted() {
+ val url = "about:addons"
+ val directions = SearchFragmentDirections.actionGlobalAddonsManagementFragment()
+
+ controller.handleUrlCommitted(url)
+
+ verify { navController.navigate(directions) }
+ }
+
@Test
fun handleMozillaUrlCommitted() {
val url = "moz://a"
@@ -265,7 +277,7 @@ class DefaultSearchControllerTest {
controller.handleClickSearchEngineSettings()
- verify { navController.navigateSafe(R.id.searchEngineFragment, directions) }
+ verify { navController.navigate(directions) }
}
@Test
diff --git a/app/src/test/java/org/mozilla/fenix/searchdialog/SearchDialogControllerTest.kt b/app/src/test/java/org/mozilla/fenix/searchdialog/SearchDialogControllerTest.kt
index cfa3649c6..762f5e866 100644
--- a/app/src/test/java/org/mozilla/fenix/searchdialog/SearchDialogControllerTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/searchdialog/SearchDialogControllerTest.kt
@@ -29,7 +29,6 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.components.metrics.MetricsUtils
-import org.mozilla.fenix.ext.navigateSafe
import org.mozilla.fenix.search.SearchFragmentAction
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.utils.Settings
@@ -56,6 +55,9 @@ class SearchDialogControllerTest {
every { store.state.tabId } returns "test-tab-id"
every { store.state.searchEngineSource.searchEngine } returns searchEngine
every { sessionManager.select(any()) } just Runs
+ every { navController.currentDestination } returns mockk {
+ every { id } returns R.id.searchDialogFragment
+ }
every { MetricsUtils.createSearchEvent(searchEngine, activity, any()) } returns null
controller = SearchDialogController(
@@ -120,6 +122,16 @@ class SearchDialogControllerTest {
}
}
+ @Test
+ fun handleAddonsUrlCommitted() {
+ val url = "about:addons"
+ val directions = SearchDialogFragmentDirections.actionGlobalAddonsManagementFragment()
+
+ controller.handleUrlCommitted(url)
+
+ verify { navController.navigate(directions) }
+ }
+
@Test
fun handleMozillaUrlCommitted() {
val url = "moz://a"
@@ -266,7 +278,7 @@ class SearchDialogControllerTest {
controller.handleClickSearchEngineSettings()
- verify { navController.navigateSafe(R.id.searchEngineFragment, directions) }
+ verify { navController.navigate(directions) }
}
@Test
diff --git a/app/src/test/java/org/mozilla/fenix/settings/about/AboutLibrariesActivityTest.kt b/app/src/test/java/org/mozilla/fenix/settings/about/AboutLibrariesFragmentTest.kt
similarity index 64%
rename from app/src/test/java/org/mozilla/fenix/settings/about/AboutLibrariesActivityTest.kt
rename to app/src/test/java/org/mozilla/fenix/settings/about/AboutLibrariesFragmentTest.kt
index 140dc44ae..5b7410022 100644
--- a/app/src/test/java/org/mozilla/fenix/settings/about/AboutLibrariesActivityTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/settings/about/AboutLibrariesFragmentTest.kt
@@ -7,30 +7,34 @@ package org.mozilla.fenix.settings.about
import android.widget.ListView
import android.widget.TextView
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.createAddedTestFragmentInNavHostActivity
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
-import org.robolectric.Robolectric
import org.robolectric.Shadows.shadowOf
import org.robolectric.shadows.ShadowAlertDialog
@RunWith(FenixRobolectricTestRunner::class)
-class AboutLibrariesActivityTest {
- @Test
- fun `activity should display licenses`() {
- val activity = Robolectric.buildActivity(AboutLibrariesActivity::class.java).create().get()
- val listView = activity.findViewById(R.id.about_libraries_listview)
+class AboutLibrariesFragmentTest {
+ private lateinit var fragment: AboutLibrariesFragment
+ private lateinit var librariesListView: ListView
+
+ @Before
+ fun setup() {
+ fragment = createAddedTestFragmentInNavHostActivity { AboutLibrariesFragment() }
+ librariesListView = fragment.requireView().findViewById(R.id.about_libraries_listview)
+ }
- assertTrue(0 < listView.count)
+ @Test
+ fun `fragment should display licenses`() {
+ assertTrue(0 < librariesListView.count)
}
@Test
fun `item click should open license dialog`() {
- val activity = Robolectric.buildActivity(AboutLibrariesActivity::class.java).create().get()
-
- val listView = activity.findViewById(R.id.about_libraries_listview)
- val listViewShadow = shadowOf(listView)
+ val listViewShadow = shadowOf(librariesListView)
listViewShadow.clickFirstItemContainingText("org.mozilla.geckoview:geckoview")
val alertDialogShadow = ShadowAlertDialog.getLatestDialog()
diff --git a/app/src/test/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceViewTest.kt b/app/src/test/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceViewTest.kt
index fb2de9d7b..a6b68ecb4 100644
--- a/app/src/test/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceViewTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceViewTest.kt
@@ -12,24 +12,18 @@ import io.mockk.impl.annotations.MockK
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkConstructor
-import io.mockk.mockkStatic
import io.mockk.slot
import io.mockk.unmockkConstructor
-import io.mockk.unmockkStatic
import io.mockk.verify
import mozilla.components.concept.sync.AccountObserver
-import mozilla.components.feature.accounts.FirefoxAccountsAuthFeature
import mozilla.components.service.fxa.SyncEngine
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.fxa.manager.SyncEnginesStorage
-import mozilla.components.support.ktx.android.content.hasCamera
import org.junit.After
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mozilla.fenix.R
-import org.mozilla.fenix.components.metrics.Event
-import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragmentDirections
class SyncLoginsPreferenceViewTest {
@@ -38,8 +32,6 @@ class SyncLoginsPreferenceViewTest {
@MockK private lateinit var lifecycleOwner: LifecycleOwner
@MockK private lateinit var accountManager: FxaAccountManager
@MockK(relaxed = true) private lateinit var navController: NavController
- @MockK(relaxed = true) private lateinit var accountsAuthFeature: FirefoxAccountsAuthFeature
- @MockK(relaxed = true) private lateinit var metrics: MetricController
private lateinit var accountObserver: CapturingSlot
private lateinit var clickListener: CapturingSlot
@@ -95,11 +87,9 @@ class SyncLoginsPreferenceViewTest {
}
@Test
- fun `needs login if account does not exist and device has camera`() {
+ fun `needs login if account does not exist`() {
every { accountManager.authenticatedAccount() } returns null
every { accountManager.accountNeedsReauth() } returns false
- mockkStatic("mozilla.components.support.ktx.android.content.ContextKt")
- every { any().hasCamera() } returns true
createView()
verify { syncLoginsPreference.summary = "Sign in to Sync" }
@@ -110,26 +100,6 @@ class SyncLoginsPreferenceViewTest {
SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToTurnOnSyncFragment()
)
}
-
- unmockkStatic("mozilla.components.support.ktx.android.content.ContextKt")
- }
-
- @Test
- fun `needs login if account does not exist and device does not have camera`() {
- every { accountManager.authenticatedAccount() } returns null
- every { accountManager.accountNeedsReauth() } returns false
- createView()
- mockkStatic("mozilla.components.support.ktx.android.content.ContextKt")
- every { any().hasCamera() } returns false
-
- verify { syncLoginsPreference.summary = "Sign in to Sync" }
- assertTrue(clickListener.captured.onPreferenceClick(syncLoginsPreference))
- verify {
- accountsAuthFeature.beginAuthentication(any())
- metrics.track(Event.SyncAuthUseEmail)
- }
-
- unmockkStatic("mozilla.components.support.ktx.android.content.ContextKt")
}
@Test
@@ -171,8 +141,6 @@ class SyncLoginsPreferenceViewTest {
syncLoginsPreference,
lifecycleOwner,
accountManager,
- navController,
- accountsAuthFeature,
- metrics
+ navController
)
}
diff --git a/app/src/test/java/org/mozilla/fenix/sync/ListenerDelegateTest.kt b/app/src/test/java/org/mozilla/fenix/sync/ListenerDelegateTest.kt
new file mode 100644
index 000000000..f1863bd60
--- /dev/null
+++ b/app/src/test/java/org/mozilla/fenix/sync/ListenerDelegateTest.kt
@@ -0,0 +1,26 @@
+/* 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.sync
+
+import io.mockk.mockk
+import io.mockk.verify
+import mozilla.components.feature.syncedtabs.view.SyncedTabsView
+import org.junit.Test
+
+class ListenerDelegateTest {
+ @Test
+ fun `delegate invokes nullable listener`() {
+ val listener: SyncedTabsView.Listener? = mockk(relaxed = true)
+ val delegate = ListenerDelegate { listener }
+
+ delegate.onRefresh()
+
+ verify { listener?.onRefresh() }
+
+ delegate.onTabClicked(mockk())
+
+ verify { listener?.onTabClicked(any()) }
+ }
+}
diff --git a/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsAdapterTest.kt b/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsAdapterTest.kt
index e22bbb181..f1f7f3fea 100644
--- a/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsAdapterTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsAdapterTest.kt
@@ -11,6 +11,7 @@ import mozilla.components.browser.storage.sync.SyncedDeviceTabs
import mozilla.components.browser.storage.sync.Tab
import mozilla.components.browser.storage.sync.TabEntry
import mozilla.components.concept.sync.DeviceType
+import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -21,7 +22,7 @@ import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class SyncedTabsAdapterTest {
- private lateinit var listener: (Tab) -> Unit
+ private lateinit var listener: SyncedTabsView.Listener
private lateinit var adapter: SyncedTabsAdapter
private val oneTabDevice = SyncedDeviceTabs(
@@ -77,10 +78,12 @@ class SyncedTabsAdapterTest {
fun `updateData() adds items for each device and tab`() {
assertEquals(0, adapter.itemCount)
- adapter.updateData(listOf(
- oneTabDevice,
- threeTabDevice
- ))
+ adapter.updateData(
+ listOf(
+ oneTabDevice,
+ threeTabDevice
+ )
+ )
assertEquals(5, adapter.itemCount)
assertEquals(SyncedTabsViewHolder.DeviceViewHolder.LAYOUT_ID, adapter.getItemViewType(0))
diff --git a/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsLayoutTest.kt b/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsLayoutTest.kt
index a04bfbb51..8c1f6514a 100644
--- a/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsLayoutTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsLayoutTest.kt
@@ -4,16 +4,10 @@
package org.mozilla.fenix.sync
-import androidx.navigation.NavController
-import io.mockk.mockk
import mozilla.components.feature.syncedtabs.view.SyncedTabsView.ErrorType
-import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
-import org.junit.Assert.assertNotNull
-import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
-import org.mozilla.fenix.R
class SyncedTabsLayoutTest {
@@ -25,73 +19,4 @@ class SyncedTabsLayoutTest {
assertFalse(SyncedTabsLayout.pullToRefreshEnableState(ErrorType.SYNC_NEEDS_REAUTHENTICATION))
assertFalse(SyncedTabsLayout.pullToRefreshEnableState(ErrorType.SYNC_UNAVAILABLE))
}
-
- @Test
- fun `string resource for error`() {
- assertEquals(
- R.string.synced_tabs_connect_another_device,
- SyncedTabsLayout.stringResourceForError(ErrorType.MULTIPLE_DEVICES_UNAVAILABLE)
- )
- assertEquals(
- R.string.synced_tabs_enable_tab_syncing,
- SyncedTabsLayout.stringResourceForError(ErrorType.SYNC_ENGINE_UNAVAILABLE)
- )
- assertEquals(
- R.string.synced_tabs_sign_in_message,
- SyncedTabsLayout.stringResourceForError(ErrorType.SYNC_UNAVAILABLE)
- )
- assertEquals(
- R.string.synced_tabs_reauth,
- SyncedTabsLayout.stringResourceForError(ErrorType.SYNC_NEEDS_REAUTHENTICATION)
- )
- assertEquals(
- R.string.synced_tabs_no_tabs,
- SyncedTabsLayout.stringResourceForError(ErrorType.NO_TABS_AVAILABLE)
- )
- }
-
- @Test
- fun `get error item`() {
- val navController = mockk()
-
- var errorItem = SyncedTabsLayout.getErrorItem(
- navController,
- ErrorType.MULTIPLE_DEVICES_UNAVAILABLE,
- R.string.synced_tabs_connect_another_device
- )
- assertNull((errorItem as SyncedTabsAdapter.AdapterItem.Error).navController)
- assertEquals(R.string.synced_tabs_connect_another_device, errorItem.descriptionResId)
-
- errorItem = SyncedTabsLayout.getErrorItem(
- navController,
- ErrorType.SYNC_ENGINE_UNAVAILABLE,
- R.string.synced_tabs_enable_tab_syncing
- )
- assertNull((errorItem as SyncedTabsAdapter.AdapterItem.Error).navController)
- assertEquals(R.string.synced_tabs_enable_tab_syncing, errorItem.descriptionResId)
-
- errorItem = SyncedTabsLayout.getErrorItem(
- navController,
- ErrorType.SYNC_NEEDS_REAUTHENTICATION,
- R.string.synced_tabs_reauth
- )
- assertNull((errorItem as SyncedTabsAdapter.AdapterItem.Error).navController)
- assertEquals(R.string.synced_tabs_reauth, errorItem.descriptionResId)
-
- errorItem = SyncedTabsLayout.getErrorItem(
- navController,
- ErrorType.NO_TABS_AVAILABLE,
- R.string.synced_tabs_no_tabs
- )
- assertNull((errorItem as SyncedTabsAdapter.AdapterItem.Error).navController)
- assertEquals(R.string.synced_tabs_no_tabs, errorItem.descriptionResId)
-
- errorItem = SyncedTabsLayout.getErrorItem(
- navController,
- ErrorType.SYNC_UNAVAILABLE,
- R.string.synced_tabs_sign_in_message
- )
- assertNotNull((errorItem as SyncedTabsAdapter.AdapterItem.Error).navController)
- assertEquals(R.string.synced_tabs_sign_in_message, errorItem.descriptionResId)
- }
}
diff --git a/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsViewHolderTest.kt
index b81a0fc3b..3f8b06fd2 100644
--- a/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsViewHolderTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/sync/SyncedTabsViewHolderTest.kt
@@ -7,15 +7,18 @@ package org.mozilla.fenix.sync
import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
+import io.mockk.Called
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.sync_tabs_list_item.view.*
import kotlinx.android.synthetic.main.view_synced_tabs_group.view.*
+import kotlinx.android.synthetic.main.view_synced_tabs_title.view.*
import mozilla.components.browser.storage.sync.Tab
import mozilla.components.browser.storage.sync.TabEntry
import mozilla.components.concept.sync.Device
import mozilla.components.concept.sync.DeviceType
+import mozilla.components.feature.syncedtabs.view.SyncedTabsView
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -32,6 +35,10 @@ class SyncedTabsViewHolderTest {
private lateinit var deviceViewHolder: SyncedTabsViewHolder.DeviceViewHolder
private lateinit var deviceView: View
private lateinit var deviceViewGroupName: TextView
+ private lateinit var titleView: View
+ private lateinit var titleViewHolder: SyncedTabsViewHolder.TitleViewHolder
+ private lateinit var noTabsView: View
+ private lateinit var noTabsViewHolder: SyncedTabsViewHolder.NoTabsViewHolder
private val tab = Tab(
history = listOf(
@@ -59,6 +66,12 @@ class SyncedTabsViewHolderTest {
every { synced_tabs_group_name } returns deviceViewGroupName
}
deviceViewHolder = SyncedTabsViewHolder.DeviceViewHolder(deviceView)
+
+ titleView = inflater.inflate(SyncedTabsViewHolder.TitleViewHolder.LAYOUT_ID, null)
+ titleViewHolder = SyncedTabsViewHolder.TitleViewHolder(titleView)
+
+ noTabsView = inflater.inflate(SyncedTabsViewHolder.NoTabsViewHolder.LAYOUT_ID, null)
+ noTabsViewHolder = SyncedTabsViewHolder.NoTabsViewHolder(noTabsView)
}
@Test
@@ -71,11 +84,11 @@ class SyncedTabsViewHolderTest {
@Test
fun `TabViewHolder calls interactor on click`() {
- val interactor = mockk<(Tab) -> Unit>(relaxed = true)
+ val interactor = mockk(relaxed = true)
tabViewHolder.bind(SyncedTabsAdapter.AdapterItem.Tab(tab), interactor)
tabView.performClick()
- verify { interactor(tab) }
+ verify { interactor.onTabClicked(tab) }
}
@Test
@@ -109,4 +122,28 @@ class SyncedTabsViewHolderTest {
)
}
}
+
+ @Test
+ fun `TitleViewHolder calls interactor refresh`() {
+ val interactor = mockk(relaxed = true)
+ titleViewHolder.bind(SyncedTabsAdapter.AdapterItem.Title, interactor)
+
+ titleView.findViewById(R.id.refresh_icon).performClick()
+
+ verify { interactor.onRefresh() }
+ }
+
+ @Test
+ fun `NoTabsViewHolder does nothing`() {
+ val device = mockk {
+ every { displayName } returns "Charcoal"
+ every { deviceType } returns DeviceType.DESKTOP
+ }
+ val interactor = mockk(relaxed = true)
+ noTabsViewHolder.bind(SyncedTabsAdapter.AdapterItem.NoTabs(device), interactor)
+
+ titleView.performClick()
+
+ verify { interactor wasNot Called }
+ }
}
diff --git a/app/src/test/java/org/mozilla/fenix/sync/ext/ErrorTypeKtTest.kt b/app/src/test/java/org/mozilla/fenix/sync/ext/ErrorTypeKtTest.kt
new file mode 100644
index 000000000..180ed0878
--- /dev/null
+++ b/app/src/test/java/org/mozilla/fenix/sync/ext/ErrorTypeKtTest.kt
@@ -0,0 +1,72 @@
+package org.mozilla.fenix.sync.ext
+
+import org.junit.Test
+import androidx.navigation.NavController
+import io.mockk.mockk
+import mozilla.components.feature.syncedtabs.view.SyncedTabsView.ErrorType
+import org.mozilla.fenix.R
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertEquals
+
+class ErrorTypeKtTest {
+
+ @Test
+ fun `string resource for error`() {
+ assertEquals(
+ R.string.synced_tabs_connect_another_device,
+ ErrorType.MULTIPLE_DEVICES_UNAVAILABLE.toStringRes()
+ )
+ assertEquals(
+ R.string.synced_tabs_enable_tab_syncing,
+ ErrorType.SYNC_ENGINE_UNAVAILABLE.toStringRes()
+ )
+ assertEquals(
+ R.string.synced_tabs_sign_in_message,
+ ErrorType.SYNC_UNAVAILABLE.toStringRes()
+ )
+ assertEquals(
+ R.string.synced_tabs_reauth,
+ ErrorType.SYNC_NEEDS_REAUTHENTICATION.toStringRes()
+ )
+ assertEquals(
+ R.string.synced_tabs_no_tabs,
+ ErrorType.NO_TABS_AVAILABLE.toStringRes()
+ )
+ }
+
+ @Test
+ fun `get error item`() {
+ val navController = mockk()
+
+ var errorItem = ErrorType.MULTIPLE_DEVICES_UNAVAILABLE.toAdapterItem(
+ R.string.synced_tabs_connect_another_device, navController
+ )
+ assertNull(errorItem.navController)
+ assertEquals(R.string.synced_tabs_connect_another_device, errorItem.descriptionResId)
+
+ errorItem = ErrorType.SYNC_ENGINE_UNAVAILABLE.toAdapterItem(
+ R.string.synced_tabs_enable_tab_syncing, navController
+ )
+ assertNull(errorItem.navController)
+ assertEquals(R.string.synced_tabs_enable_tab_syncing, errorItem.descriptionResId)
+
+ errorItem = ErrorType.SYNC_NEEDS_REAUTHENTICATION.toAdapterItem(
+ R.string.synced_tabs_reauth, navController
+ )
+ assertNull(errorItem.navController)
+ assertEquals(R.string.synced_tabs_reauth, errorItem.descriptionResId)
+
+ errorItem = ErrorType.NO_TABS_AVAILABLE.toAdapterItem(
+ R.string.synced_tabs_no_tabs, navController
+ )
+ assertNull(errorItem.navController)
+ assertEquals(R.string.synced_tabs_no_tabs, errorItem.descriptionResId)
+
+ errorItem = ErrorType.SYNC_UNAVAILABLE.toAdapterItem(
+ R.string.synced_tabs_sign_in_message, navController
+ )
+ assertNotNull(errorItem.navController)
+ assertEquals(R.string.synced_tabs_sign_in_message, errorItem.descriptionResId)
+ }
+}
diff --git a/app/src/test/java/org/mozilla/fenix/sync/ext/SyncedTabsAdapterKtTest.kt b/app/src/test/java/org/mozilla/fenix/sync/ext/SyncedTabsAdapterKtTest.kt
new file mode 100644
index 000000000..aa568acdd
--- /dev/null
+++ b/app/src/test/java/org/mozilla/fenix/sync/ext/SyncedTabsAdapterKtTest.kt
@@ -0,0 +1,94 @@
+/* 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.sync.ext
+
+import io.mockk.every
+import io.mockk.mockk
+import mozilla.components.browser.storage.sync.SyncedDeviceTabs
+import mozilla.components.browser.storage.sync.Tab
+import mozilla.components.browser.storage.sync.TabEntry
+import mozilla.components.concept.sync.DeviceType
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.mozilla.fenix.sync.SyncedTabsAdapter
+
+class SyncedTabsAdapterKtTest {
+ private val noTabDevice = SyncedDeviceTabs(
+ device = mockk {
+ every { displayName } returns "Charcoal"
+ every { deviceType } returns DeviceType.DESKTOP
+ },
+ tabs = emptyList()
+ )
+
+ private val oneTabDevice = SyncedDeviceTabs(
+ device = mockk {
+ every { displayName } returns "Charcoal"
+ every { deviceType } returns DeviceType.DESKTOP
+ },
+ tabs = listOf(Tab(
+ history = listOf(TabEntry(
+ title = "Mozilla",
+ url = "https://mozilla.org",
+ iconUrl = null
+ )),
+ active = 0,
+ lastUsed = 0L
+ ))
+ )
+
+ private val twoTabDevice = SyncedDeviceTabs(
+ device = mockk {
+ every { displayName } returns "Emerald"
+ every { deviceType } returns DeviceType.MOBILE
+ },
+ tabs = listOf(
+ Tab(
+ history = listOf(TabEntry(
+ title = "Mozilla",
+ url = "https://mozilla.org",
+ iconUrl = null
+ )),
+ active = 0,
+ lastUsed = 0L
+ ),
+ Tab(
+ history = listOf(
+ TabEntry(
+ title = "Firefox",
+ url = "https://firefox.com",
+ iconUrl = null
+ )
+ ),
+ active = 0,
+ lastUsed = 0L
+ )
+ )
+ )
+
+ @Test
+ fun `verify ordering of adapter items`() {
+ val syncedDeviceList = listOf(oneTabDevice, twoTabDevice)
+ val adapterData = syncedDeviceList.toAdapterList()
+
+ assertEquals(5, adapterData.count())
+ assertTrue(adapterData[0] is SyncedTabsAdapter.AdapterItem.Device)
+ assertTrue(adapterData[1] is SyncedTabsAdapter.AdapterItem.Tab)
+ assertTrue(adapterData[2] is SyncedTabsAdapter.AdapterItem.Device)
+ assertTrue(adapterData[3] is SyncedTabsAdapter.AdapterItem.Tab)
+ assertTrue(adapterData[4] is SyncedTabsAdapter.AdapterItem.Tab)
+ }
+
+ @Test
+ fun `verify no tabs displayed`() {
+ val syncedDeviceList = listOf(noTabDevice)
+ val adapterData = syncedDeviceList.toAdapterList()
+
+ assertEquals(2, adapterData.count())
+ assertTrue(adapterData[0] is SyncedTabsAdapter.AdapterItem.Device)
+ assertTrue(adapterData[1] is SyncedTabsAdapter.AdapterItem.NoTabs)
+ }
+}
diff --git a/app/src/test/java/org/mozilla/fenix/tabhistory/TabHistoryControllerTest.kt b/app/src/test/java/org/mozilla/fenix/tabhistory/TabHistoryControllerTest.kt
index 1f9099245..0502b803f 100644
--- a/app/src/test/java/org/mozilla/fenix/tabhistory/TabHistoryControllerTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/tabhistory/TabHistoryControllerTest.kt
@@ -8,15 +8,17 @@ import androidx.navigation.NavController
import io.mockk.mockk
import io.mockk.verify
import mozilla.components.browser.session.SessionManager
+import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.feature.session.SessionUseCases
import org.junit.Test
import org.mozilla.fenix.R
class TabHistoryControllerTest {
+ private val store: BrowserStore = mockk(relaxed = true)
private val sessionManager: SessionManager = mockk(relaxed = true)
private val navController: NavController = mockk(relaxed = true)
- private val sessionUseCases = SessionUseCases(sessionManager)
+ private val sessionUseCases = SessionUseCases(store, sessionManager)
private val goToHistoryIndexUseCase = sessionUseCases.goToHistoryIndex
private val controller = DefaultTabHistoryController(
navController = navController,
diff --git a/app/src/test/java/org/mozilla/fenix/tabtray/DefaultTabTrayControllerTest.kt b/app/src/test/java/org/mozilla/fenix/tabtray/DefaultTabTrayControllerTest.kt
index 57ce54da4..c95c8bef4 100644
--- a/app/src/test/java/org/mozilla/fenix/tabtray/DefaultTabTrayControllerTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/tabtray/DefaultTabTrayControllerTest.kt
@@ -26,6 +26,8 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
+import org.mozilla.fenix.BrowserDirection
+import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
@@ -34,6 +36,7 @@ import org.mozilla.fenix.ext.sessionsOfType
@OptIn(ExperimentalCoroutinesApi::class)
class DefaultTabTrayControllerTest {
+ private val activity: HomeActivity = mockk(relaxed = true)
private val profiler: Profiler? = mockk(relaxed = true)
private val navController: NavController = mockk()
private val sessionManager: SessionManager = mockk(relaxed = true)
@@ -81,6 +84,7 @@ class DefaultTabTrayControllerTest {
every { tabCollection.title } returns "Collection title"
controller = DefaultTabTrayController(
+ activity = activity,
profiler = profiler,
sessionManager = sessionManager,
browsingModeManager = browsingModeManager,
@@ -156,6 +160,15 @@ class DefaultTabTrayControllerTest {
}
}
+ @Test
+ fun onSyncedTabClicked() {
+ controller.onSyncedTabClicked(mockk(relaxed = true))
+
+ verify {
+ activity.openToBrowserAndLoad(any(), true, BrowserDirection.FromTabTray)
+ }
+ }
+
@Test
fun handleBackPressed() {
every { tabTrayFragmentStore.state.mode } returns TabTrayDialogFragmentState.Mode.MultiSelect(
diff --git a/app/src/test/java/org/mozilla/fenix/tabtray/SyncedTabsControllerTest.kt b/app/src/test/java/org/mozilla/fenix/tabtray/SyncedTabsControllerTest.kt
new file mode 100644
index 000000000..6ff967c84
--- /dev/null
+++ b/app/src/test/java/org/mozilla/fenix/tabtray/SyncedTabsControllerTest.kt
@@ -0,0 +1,133 @@
+package org.mozilla.fenix.tabtray
+
+import android.view.LayoutInflater
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import androidx.recyclerview.widget.ConcatAdapter
+import io.mockk.Called
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineDispatcher
+import kotlinx.coroutines.test.runBlockingTest
+import mozilla.components.browser.storage.sync.SyncedDeviceTabs
+import mozilla.components.feature.syncedtabs.view.SyncedTabsView.ErrorType
+import mozilla.components.support.test.ext.joinBlocking
+import mozilla.components.support.test.robolectric.testContext
+import mozilla.components.support.test.rule.MainCoroutineRule
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mozilla.fenix.R
+import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
+import org.mozilla.fenix.sync.SyncedTabsViewHolder
+import org.mozilla.fenix.tabtray.TabTrayDialogFragmentAction.EnterMultiSelectMode
+import org.mozilla.fenix.tabtray.TabTrayDialogFragmentAction.ExitMultiSelectMode
+import org.mozilla.fenix.tabtray.TabTrayDialogFragmentState.Mode
+
+@ExperimentalCoroutinesApi
+@RunWith(FenixRobolectricTestRunner::class)
+class SyncedTabsControllerTest {
+
+ private val testDispatcher = TestCoroutineDispatcher()
+ @get:Rule
+ val coroutinesTestRule = MainCoroutineRule(testDispatcher)
+
+ private lateinit var view: View
+ private lateinit var controller: SyncedTabsController
+ private lateinit var lifecycleOwner: LifecycleOwner
+ private lateinit var lifecycle: LifecycleRegistry
+ private lateinit var concatAdapter: ConcatAdapter
+ private lateinit var store: TabTrayDialogFragmentStore
+
+ @Before
+ fun setup() = runBlockingTest {
+ lifecycleOwner = mockk()
+ lifecycle = LifecycleRegistry(lifecycleOwner)
+ lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START)
+ every { lifecycleOwner.lifecycle } returns lifecycle
+
+ concatAdapter = mockk()
+ every { concatAdapter.addAdapter(any(), any()) } returns true
+ every { concatAdapter.removeAdapter(any()) } returns true
+
+ store = TabTrayDialogFragmentStore(
+ initialState = TabTrayDialogFragmentState(
+ mode = Mode.Normal,
+ browserState = mockk(relaxed = true)
+ )
+ )
+
+ view = LayoutInflater.from(testContext).inflate(R.layout.about_list_item, null)
+ controller =
+ SyncedTabsController(lifecycleOwner, view, store, concatAdapter, coroutineContext)
+ }
+
+ @Test
+ fun `display synced tabs in reverse`() {
+ val tabs = listOf(
+ SyncedDeviceTabs(
+ device = mockk(relaxed = true),
+ tabs = listOf(
+ mockk(relaxed = true),
+ mockk(relaxed = true)
+ )
+ )
+ )
+
+ controller.displaySyncedTabs(tabs)
+
+ val itemCount = controller.adapter.itemCount
+
+ // title + device name + 2 tabs
+ assertEquals(4, itemCount)
+ assertEquals(
+ SyncedTabsViewHolder.TitleViewHolder.LAYOUT_ID,
+ controller.adapter.getItemViewType(itemCount - 1)
+ )
+ assertEquals(
+ SyncedTabsViewHolder.DeviceViewHolder.LAYOUT_ID,
+ controller.adapter.getItemViewType(itemCount - 2)
+ )
+ assertEquals(
+ SyncedTabsViewHolder.TabViewHolder.LAYOUT_ID,
+ controller.adapter.getItemViewType(itemCount - 3)
+ )
+ assertEquals(
+ SyncedTabsViewHolder.TabViewHolder.LAYOUT_ID,
+ controller.adapter.getItemViewType(itemCount - 4)
+ )
+ }
+
+ @Test
+ fun `show error when we go kaput`() {
+ controller.onError(ErrorType.SYNC_NEEDS_REAUTHENTICATION)
+
+ assertEquals(1, controller.adapter.itemCount)
+ assertEquals(
+ SyncedTabsViewHolder.ErrorViewHolder.LAYOUT_ID,
+ controller.adapter.getItemViewType(0)
+ )
+ }
+
+ @Test
+ fun `do nothing on init, drop first event`() {
+ verify { concatAdapter wasNot Called }
+ }
+
+ @Test
+ fun `concatAdapter updated on mode changes`() = testDispatcher.runBlockingTest {
+ store.dispatch(EnterMultiSelectMode).joinBlocking()
+
+ verify { concatAdapter.removeAdapter(any()) }
+
+ store.dispatch(ExitMultiSelectMode).joinBlocking()
+
+ verify { concatAdapter.addAdapter(0, any()) }
+ }
+}
diff --git a/app/src/test/java/org/mozilla/fenix/tabtray/TabTrayFragmentInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/tabtray/TabTrayFragmentInteractorTest.kt
index 8a459f73f..575b3a4ec 100644
--- a/app/src/test/java/org/mozilla/fenix/tabtray/TabTrayFragmentInteractorTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/tabtray/TabTrayFragmentInteractorTest.kt
@@ -53,6 +53,12 @@ class TabTrayFragmentInteractorTest {
verify { controller.onCloseAllTabsClicked(true) }
}
+ @Test
+ fun onSyncedTabClicked() {
+ interactor.onSyncedTabClicked(mockk(relaxed = true))
+ verify { controller.onSyncedTabClicked(any()) }
+ }
+
@Test
fun onBackPressed() {
interactor.onBackPressed()
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 6bb0b26b5..6e1e9a51d 100644
--- a/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/utils/SettingsTest.kt
@@ -289,6 +289,19 @@ class SettingsTest {
assertFalse(settings.shouldUseTrackingProtection)
}
+ @Test
+ fun shouldShowCollectionsPlaceholderOnHome() {
+ // When
+ // Then
+ assertTrue(settings.showCollectionsPlaceholderOnHome)
+
+ // When
+ settings.showCollectionsPlaceholderOnHome = false
+
+ // Then
+ assertFalse(settings.showCollectionsPlaceholderOnHome)
+ }
+
@Test
fun shouldSetOpenInAppOpened() {
// When
diff --git a/app/src/test/java/org/mozilla/fenix/widget/VoiceSearchActivityTest.kt b/app/src/test/java/org/mozilla/fenix/widget/VoiceSearchActivityTest.kt
index 6ba449f6b..504c5fbe1 100644
--- a/app/src/test/java/org/mozilla/fenix/widget/VoiceSearchActivityTest.kt
+++ b/app/src/test/java/org/mozilla/fenix/widget/VoiceSearchActivityTest.kt
@@ -7,12 +7,14 @@ package org.mozilla.fenix.widget
import android.app.Activity
import android.content.ComponentName
import android.content.Intent
+import android.content.IntentFilter
import android.os.Bundle
import android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH
import android.speech.RecognizerIntent.EXTRA_LANGUAGE_MODEL
import android.speech.RecognizerIntent.EXTRA_RESULTS
import android.speech.RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
+import androidx.test.core.app.ApplicationProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -21,6 +23,7 @@ import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mozilla.fenix.FenixApplication
import org.mozilla.fenix.HomeActivity.Companion.OPEN_TO_BROWSER_AND_LOAD
import org.mozilla.fenix.IntentReceiverActivity
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@@ -28,7 +31,7 @@ import org.mozilla.fenix.widget.VoiceSearchActivity.Companion.PREVIOUS_INTENT
import org.mozilla.fenix.widget.VoiceSearchActivity.Companion.SPEECH_PROCESSING
import org.mozilla.fenix.widget.VoiceSearchActivity.Companion.SPEECH_REQUEST_CODE
import org.robolectric.Robolectric
-import org.robolectric.Shadows
+import org.robolectric.Shadows.shadowOf
import org.robolectric.android.controller.ActivityController
import org.robolectric.shadows.ShadowActivity
@@ -37,7 +40,7 @@ import org.robolectric.shadows.ShadowActivity
class VoiceSearchActivityTest {
private lateinit var controller: ActivityController
- private lateinit var activity: Activity
+ private lateinit var activity: VoiceSearchActivity
private lateinit var shadow: ShadowActivity
@Before
@@ -47,21 +50,36 @@ class VoiceSearchActivityTest {
controller = Robolectric.buildActivity(VoiceSearchActivity::class.java, intent)
activity = controller.get()
- shadow = Shadows.shadowOf(activity)
+ shadow = shadowOf(activity)
+ }
+
+ private fun allowVoiceIntentToResolveActivity() {
+ val context = ApplicationProvider.getApplicationContext()
+ val shadowPackageManager = shadowOf(context.packageManager)
+ val component = ComponentName("com.test", "Test")
+ shadowPackageManager.addActivityIfNotPresent(component)
+ shadowPackageManager.addIntentFilterForActivity(
+ component,
+ IntentFilter(ACTION_RECOGNIZE_SPEECH).apply { addCategory(Intent.CATEGORY_DEFAULT) })
}
@Test
fun `process intent with speech processing set to true`() {
+ allowVoiceIntentToResolveActivity()
controller.create()
val intentForResult = shadow.peekNextStartedActivityForResult()
assertEquals(SPEECH_REQUEST_CODE, intentForResult.requestCode)
assertEquals(ACTION_RECOGNIZE_SPEECH, intentForResult.intent.action)
- assertEquals(LANGUAGE_MODEL_FREE_FORM, intentForResult.intent.getStringExtra(EXTRA_LANGUAGE_MODEL))
+ assertEquals(
+ LANGUAGE_MODEL_FREE_FORM,
+ intentForResult.intent.getStringExtra(EXTRA_LANGUAGE_MODEL)
+ )
}
@Test
fun `process intent with speech processing set to false`() {
+ allowVoiceIntentToResolveActivity()
val intent = Intent()
intent.putExtra(SPEECH_PROCESSING, false)
@@ -75,6 +93,7 @@ class VoiceSearchActivityTest {
@Test
fun `process null intent`() {
+ allowVoiceIntentToResolveActivity()
val controller = Robolectric.buildActivity(VoiceSearchActivity::class.java, null)
val activity = controller.get()
@@ -85,6 +104,7 @@ class VoiceSearchActivityTest {
@Test
fun `save previous intent to instance state`() {
+ allowVoiceIntentToResolveActivity()
val previousIntent = Intent().apply {
putExtra(SPEECH_PROCESSING, true)
}
@@ -101,6 +121,7 @@ class VoiceSearchActivityTest {
@Test
fun `process intent with speech processing in previous intent set to true`() {
+ allowVoiceIntentToResolveActivity()
val savedInstanceState = Bundle()
val previousIntent = Intent().apply {
putExtra(SPEECH_PROCESSING, true)
@@ -115,6 +136,7 @@ class VoiceSearchActivityTest {
@Test
fun `handle speech result`() {
+ allowVoiceIntentToResolveActivity()
controller.create()
val resultIntent = Intent().apply {
@@ -129,13 +151,17 @@ class VoiceSearchActivityTest {
val browserIntent = shadow.peekNextStartedActivity()
assertTrue(activity.isFinishing)
- assertEquals(ComponentName(activity, IntentReceiverActivity::class.java), browserIntent.component)
+ assertEquals(
+ ComponentName(activity, IntentReceiverActivity::class.java),
+ browserIntent.component
+ )
assertEquals("hello world", browserIntent.getStringExtra(SPEECH_PROCESSING))
assertTrue(browserIntent.getBooleanExtra(OPEN_TO_BROWSER_AND_LOAD, false))
}
@Test
fun `handle invalid result code`() {
+ allowVoiceIntentToResolveActivity()
controller.create()
val resultIntent = Intent()
@@ -147,4 +173,10 @@ class VoiceSearchActivityTest {
assertTrue(activity.isFinishing)
}
+
+ @Test
+ fun `handle no activity able to resolve voice intent`() {
+ controller.create()
+ assertTrue(activity.isFinishing)
+ }
}
diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt
index c429e7980..04211a13b 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 = "54.0.20200814130102"
+ const val VERSION = "57.0.20200826190111"
}
diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt
index 40933b3c0..eae088417 100644
--- a/buildSrc/src/main/java/Dependencies.kt
+++ b/buildSrc/src/main/java/Dependencies.kt
@@ -27,9 +27,7 @@ object Versions {
const val androidx_paging = "2.1.0"
const val androidx_transition = "1.3.0"
const val androidx_work = "2.2.0"
- const val androidx_dynamic_animation = "1.0.0"
const val google_material = "1.1.0"
- const val google_flexbox = "2.0.1"
const val mozilla_android_components = AndroidComponents.VERSION
@@ -44,6 +42,9 @@ object Versions {
const val google_ads_id_version = "16.0.0"
+ const val google_play_store_version = "1.8.0"
+ const val google_play_core_ktx_version = "1.8.1"
+
const val airbnb_lottie = "3.4.0"
}
@@ -172,12 +173,10 @@ object Deps {
const val androidx_recyclerview = "androidx.recyclerview:recyclerview:${Versions.androidx_recyclerview}"
const val androidx_core = "androidx.core:core:${Versions.androidx_core}"
const val androidx_core_ktx = "androidx.core:core-ktx:${Versions.androidx_core}"
- const val androidx_dynamic_animation = "androidx.dynamicanimation:dynamicanimation:${Versions.androidx_dynamic_animation}"
const val androidx_transition = "androidx.transition:transition:${Versions.androidx_transition}"
const val androidx_work_ktx = "androidx.work:work-runtime-ktx:${Versions.androidx_work}"
const val androidx_work_testing = "androidx.work:work-testing:${Versions.androidx_work}"
const val google_material = "com.google.android.material:material:${Versions.google_material}"
- const val google_flexbox = "com.google.android:flexbox:${Versions.google_flexbox}"
const val adjust = "com.adjust.sdk:adjust-android:${Versions.adjust}"
const val installreferrer = "com.android.installreferrer:installreferrer:${Versions.installreferrer}"
@@ -213,6 +212,10 @@ object Deps {
const val google_ads_id = "com.google.android.gms:play-services-ads-identifier:${Versions.google_ads_id_version}"
+ // Required for in-app reviews
+ const val google_play_store = "com.google.android.play:core:${Versions.google_play_store_version}"
+ const val google_play_core_ktx = "com.google.android.play:core-ktx:${Versions.google_play_core_ktx_version}"
+
const val lottie = "com.airbnb.android:lottie:${Versions.airbnb_lottie}"
const val detektApi = "io.gitlab.arturbosch.detekt:detekt-api:${Versions.detekt}"
diff --git a/docs/crash-reporting.md b/docs/crash-reporting.md
index 7a0c92a97..66476b138 100644
--- a/docs/crash-reporting.md
+++ b/docs/crash-reporting.md
@@ -1,6 +1,6 @@
# Crash Reporting
-Firefox Preview uses a few libraries for crash and exception reporting. This kind of reporting gives Mozilla invaluable insight as to why Firefox Preview crashes or incorrectly behaves. It is one of the key methods we use to improve the product in terms of stability.
+Firefox for Android uses a few libraries for crash and exception reporting. This kind of reporting gives Mozilla invaluable insight as to why Firefox for Android crashes or incorrectly behaves. It is one of the key methods we use to improve the product in terms of stability.
This page documents the types of crash reporting, how the various parts interact, and what kind of data is sent back to Mozilla.
@@ -8,7 +8,7 @@ Documentation for the specific libraries is included in the [Android Components
## Glean crash ping
-[Glean SDK](https://mozilla.github.io/glean/book/index.html) is a Mozilla open source telemetry library, which Firefox Preview uses to collect app telemetry. It can also collect crash counts as a labeled counter with each label corresponding to a specific type of crash (such as `native_code_crash`, `unhandled_exception`).
+[Glean SDK](https://mozilla.github.io/glean/book/index.html) is a Mozilla open source telemetry library, which Firefox for Android uses to collect app telemetry. It can also collect crash counts as a labeled counter with each label corresponding to a specific type of crash (such as `native_code_crash`, `unhandled_exception`).
The Glean crash ping format is documented [here](https://github.com/mozilla-mobile/android-components/blob/master/components/lib/crash/docs/metrics.md).
@@ -24,11 +24,11 @@ In [HomeActivity](https://github.com/mozilla-mobile/fenix/blob/master/app/src/ma
## Socorro
-[Socorro](https://wiki.mozilla.org/Socorro) is a Mozilla open source project for [crash statistics](https://crash-stats.mozilla.org/). Firefox Preview uses Socorro to track native GeckoView crashes. Crash reports contain a signature, classifications, and a number of improved fields (e.g. OS, product, version) - you can read more about what is sent in these fields in the [Socorro report documentation](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Crash_reporting/Understanding_crash_reports).
+[Socorro](https://wiki.mozilla.org/Socorro) is a Mozilla open source project for [crash statistics](https://crash-stats.mozilla.org/). Firefox for Android uses Socorro to track native GeckoView crashes. Crash reports contain a signature, classifications, and a number of improved fields (e.g. OS, product, version) - you can read more about what is sent in these fields in the [Socorro report documentation](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Crash_reporting/Understanding_crash_reports).
These crashes contain hardware information and some app metadata, but no personally identifiable information is visible in the report. A few privacy-sensitive parts are only available to users who have "minidump access", which is a relatively small number of users with specific rules they must follow.
-A sample Firefox Preview crash report can be found [here](https://crash-stats.mozilla.org/report/index/bbbcc019-f30c-4fbb-8cbd-543940190923).
+A sample Firefox for Android crash report can be found [here](https://crash-stats.mozilla.org/report/index/bbbcc019-f30c-4fbb-8cbd-543940190923).
A crash report is only sent when the user confirms and submits it through the crash reporter notification or dialog. Crash reports are never automatically sent.
@@ -40,11 +40,11 @@ A crash report is only sent when the user confirms and submits it through the cr
### High-Level Summary
-The server is hosted and maintained by Mozilla. There are no third-parties involved, all crash reports are sent directly from Firefox Preview to the Sentry server hosted by Mozilla.
+The server is hosted and maintained by Mozilla. There are no third-parties involved, all crash reports are sent directly from Firefox for Android to the Sentry server hosted by Mozilla.
On the client side Sentry is invisible. There are no parts to interact with. It reports crashes and fatal errors back to Mozilla in the background.
-On the server side there is a dashboard that the Firefox Preview team uses to look at incoming crash reports. The dashboard lets us inspect the crash report in detail and for example see where in the application the crash happened, what version of the application was used and what version of Android OS was active. Below is an overview of all the attributes that are part of a crash report.
+On the server side there is a dashboard that the Firefox for Android team uses to look at incoming crash reports. The dashboard lets us inspect the crash report in detail and for example see where in the application the crash happened, what version of the application was used and what version of Android OS was active. Below is an overview of all the attributes that are part of a crash report.
### Sentry Reports
@@ -101,7 +101,7 @@ Sentry collects basic information about the device the application is running on
### Application Information
-Sentry collects basic information about the Firefox Preview app.
+Sentry collects basic information about the Firefox for Android app.
```
"app":{
@@ -122,7 +122,7 @@ Sentry collects basic information about the Firefox Preview app.
#### Stack trace
-Every crash report contains a *stack trace*, which shows what functions in the Firefox Preview code led to this crash. It includes names of Android framework functions and Firefox Preview functions. Here's an excerpt of three lines from the stack trace:
+Every crash report contains a *stack trace*, which shows what functions in the Firefox for Android code led to this crash. It includes names of Android framework functions and Firefox for Android functions. Here's an excerpt of three lines from the stack trace:
```
"sentry.interfaces.Exception": {
@@ -158,7 +158,7 @@ Every crash report contains a *stack trace*, which shows what functions in the F
##### Exception message
-The first line of every stack trace in every crash report contains a *reason* - why did this crash happen. This reason is provided by the developers who wrote the code that decide the app is in an error state. These developers include the Firefox Preview team at Mozilla, the Android framework, the Java programming language, and any libraries Mozilla bundles to develop Firefox Preview.
+The first line of every stack trace in every crash report contains a *reason* - why did this crash happen. This reason is provided by the developers who wrote the code that decide the app is in an error state. These developers include the Firefox for Android team at Mozilla, the Android framework, the Java programming language, and any libraries Mozilla bundles to develop Firefox for Android.
Java, the Android framework, and Mozilla are diligent about making sure that no personally identifiable information is put in any of these messages. We keep them technical and to the point. We at Mozilla stay on top of our dependencies to ensure they're not including personally identifiable information as well.
@@ -167,7 +167,7 @@ Here's an example message generated by Java:
java.lang.StringIndexOutOfBoundsException: length=0; regionStart=20; regionLength=20
```
-Example of a Firefox Preview generated message:
+Example of a Firefox for Android generated message:
```
java.lang.StringIndexOutOfBoundsException: Cannot create negative-length String
```
diff --git a/docs/metrics.md b/docs/metrics.md
index fdf276a7a..3f5a30f50 100644
--- a/docs/metrics.md
+++ b/docs/metrics.md
@@ -35,8 +35,8 @@ The following metrics are added to the ping:
| Name | Type | Description | Data reviews | Extras | Expiration | [Data Sensitivity](https://wiki.mozilla.org/Firefox/Data_Collection) |
| --- | --- | --- | --- | --- | --- | --- |
-| activation.activation_id |[uuid](https://mozilla.github.io/glean/book/user/metrics/uuid.html) |An alternate identifier, not correlated with the client_id, generated once and only sent with the activation ping. |[1](https://github.com/mozilla-mobile/fenix/pull/1707#issuecomment-486972209)||2020-10-01 | |
-| activation.identifier |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |A hashed and salted version of the Google Advertising ID from the device. This will never be sent in a ping that also contains the client_id. |[1](https://github.com/mozilla-mobile/fenix/pull/1707#issuecomment-486972209)||2020-10-01 | |
+| activation.activation_id |[uuid](https://mozilla.github.io/glean/book/user/metrics/uuid.html) |An alternate identifier, not correlated with the client_id, generated once and only sent with the activation ping. |[1](https://github.com/mozilla-mobile/fenix/pull/1707#issuecomment-486972209), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |4 |
+| activation.identifier |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |A hashed and salted version of the Google Advertising ID from the device. This will never be sent in a ping that also contains the client_id. |[1](https://github.com/mozilla-mobile/fenix/pull/1707#issuecomment-486972209), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |4 |
## events
@@ -48,185 +48,182 @@ The following metrics are added to the ping:
| Name | Type | Description | Data reviews | Extras | Expiration | [Data Sensitivity](https://wiki.mozilla.org/Firefox/Data_Collection) |
| --- | --- | --- | --- | --- | --- | --- |
-| about_page.libraries_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped on "Libraries that we use" item from About page |[1](https://github.com/mozilla-mobile/fenix/pull/8047)||2020-10-01 | |
-| about_page.licensing_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped on "Licensing information" item from About page |[1](https://github.com/mozilla-mobile/fenix/pull/8047)||2020-10-01 | |
-| about_page.privacy_notice_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped on "Privacy notice" item from About page |[1](https://github.com/mozilla-mobile/fenix/pull/8047)||2020-10-01 | |
-| about_page.rights_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped on "Know your rights" item from About page |[1](https://github.com/mozilla-mobile/fenix/pull/8047)||2020-10-01 | |
-| about_page.support_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped on "Support" item from About page |[1](https://github.com/mozilla-mobile/fenix/pull/8047)||2020-10-01 | |
-| addons.open_addon_in_toolbar_menu |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user interacted with an installed add-on in the toolbar menu |[1](https://github.com/mozilla-mobile/fenix/pull/8318)|
addon_id: The id of the add-on that was interacted with in the toolbar menu
|2020-10-01 | |
-| addons.open_addons_in_settings |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user accessed "Add-ons" from the Settings |[1](https://github.com/mozilla-mobile/fenix/pull/8318)||2020-10-01 | |
-| app_theme.dark_theme_selected |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user selected Dark Theme |[1](https://github.com/mozilla-mobile/fenix/pull/7968)|
source: The source from where dark theme was selected. The source can be 'SETTINGS' or 'ONBOARDING'
|2020-10-01 | |
-| autoplay.setting_changed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user changed their autoplay setting to either block_cellular, block_audio, or block_all. |[1](https://github.com/mozilla-mobile/fenix/pull/13041#issuecomment-665777411)|
autoplay_setting: The new setting for autoplay: block_cellular, block_audio, or block_all.
|2021-02-01 | |
-| autoplay.visited_setting |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user visited the autoplay settings screen |[1](https://github.com/mozilla-mobile/fenix/pull/13041#issuecomment-665777411)||2021-02-01 | |
-| bookmarks_management.copied |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user copied a bookmark. |[1](https://github.com/mozilla-mobile/fenix/pull/1708)||2020-10-01 | |
-| bookmarks_management.edited |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user edited the title and/or URL of an existing bookmark. |[1](https://github.com/mozilla-mobile/fenix/pull/1708)||2020-10-01 | |
-| bookmarks_management.folder_add |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user added a new bookmark folder. |[1](https://github.com/mozilla-mobile/fenix/pull/1708)||2020-10-01 | |
-| bookmarks_management.folder_remove |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed a bookmark folder. |[1](https://github.com/mozilla-mobile/fenix/pull/3724)||2020-10-01 | |
-| bookmarks_management.moved |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user moved an existing bookmark or folder to another folder. |[1](https://github.com/mozilla-mobile/fenix/pull/1708)||2020-10-01 | |
-| bookmarks_management.multi_removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed multiple bookmarks at once. |[1](https://github.com/mozilla-mobile/fenix/pull/1708)||2020-10-01 | |
-| bookmarks_management.open_in_new_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a bookmark in a new tab. |[1](https://github.com/mozilla-mobile/fenix/pull/1708)||2020-10-01 | |
-| bookmarks_management.open_in_new_tabs |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened multiple bookmarks at once in new tabs. |[1](https://github.com/mozilla-mobile/fenix/pull/1708)||2020-10-01 | |
-| bookmarks_management.open_in_private_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a bookmark in a new private tab. |[1](https://github.com/mozilla-mobile/fenix/pull/1708)||2020-10-01 | |
-| bookmarks_management.open_in_private_tabs |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened multiple bookmarks at once in new private tabs. |[1](https://github.com/mozilla-mobile/fenix/pull/1708)||2020-10-01 | |
-| bookmarks_management.removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed a bookmark item. |[1](https://github.com/mozilla-mobile/fenix/pull/1708)||2020-10-01 | |
-| bookmarks_management.shared |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user shared a bookmark. |[1](https://github.com/mozilla-mobile/fenix/pull/1708)||2020-10-01 | |
-| collections.add_tab_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the "add tab" button in the three dot menu of collections |[1](https://github.com/mozilla-mobile/fenix/pull/4358)||2020-10-01 | |
-| collections.all_tabs_restored |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped "open tabs" from collection menu |[1](https://github.com/mozilla-mobile/fenix/pull/3935)||2020-10-01 | |
-| collections.long_press |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user long pressed on a tab, triggering the collection creation screen |[1](https://github.com/mozilla-mobile/fenix/pull/4358)||2020-10-01 | |
-| collections.removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped delete collection from collection menu |[1](https://github.com/mozilla-mobile/fenix/pull/3935)||2020-10-01 | |
-| collections.rename_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "rename collection" button in the three dot menu |[1](https://github.com/mozilla-mobile/fenix/pull/4539)||2020-10-01 | |
-| collections.renamed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user renamed a collection |[1](https://github.com/mozilla-mobile/fenix/pull/3935)||2020-10-01 | |
-| collections.save_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "save to collection" button on either the home or browser screen, triggering the collection creation screen to open (tab_select_opened) |[1](https://github.com/mozilla-mobile/fenix/pull/4358)|
from_screen: A string representing the screen from which the user pressed the save button. Currently one of: `browserMenu`, `homeMenu` or `home`
|2020-10-01 | |
-| collections.saved |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user saved a list of tabs to a new collection |[1](https://github.com/mozilla-mobile/fenix/pull/3935)|
tabs_open: The number of tabs open in the current session
tabs_selected: The number of tabs added to the collection
|2020-10-01 | |
-| collections.shared |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped share collection |[1](https://github.com/mozilla-mobile/fenix/pull/3935)||2020-10-01 | |
-| collections.tab_removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped remove tab from collection tab list |[1](https://github.com/mozilla-mobile/fenix/pull/3935)||2020-10-01 | |
-| collections.tab_restored |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user restored a tab from collection tab list |[1](https://github.com/mozilla-mobile/fenix/pull/3935)||2020-10-01 | |
-| collections.tab_select_opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the select tabs screen (the first step of the collection creation flow) |[1](https://github.com/mozilla-mobile/fenix/pull/3935)||2020-10-01 | |
-| collections.tabs_added |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user saved a list of tabs to an existing collection |[1](https://github.com/mozilla-mobile/fenix/pull/3935)|
tabs_open: The number of tabs open in the current session
tabs_selected: The number of tabs added to the collection
|2020-10-01 | |
-| context_menu.item_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped an item in the browsers context menu |[1](https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010)|
named: The name of the item that was tapped. Available items are: ``` open_in_new_tab, open_in_private_tab, open_image_in_new_tab, save_image, share_link, copy_link, copy_image_location ```
|2020-10-01 | |
-| contextual_hint.tracking_protection.dismiss |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The enhanced tracking protection contextual hint was dismissed by pressing the close button |[1](https://github.com/mozilla-mobile/fenix/pull/11923)||2020-10-01 | |
-| contextual_hint.tracking_protection.display |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The enhanced tracking protection contextual hint was displayed. |[1](https://github.com/mozilla-mobile/fenix/pull/11923)||2020-10-01 | |
-| contextual_hint.tracking_protection.inside_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user tapped inside of the etp contextual hint (which brings up the etp panel for this site). |[1](https://github.com/mozilla-mobile/fenix/pull/11923)||2020-10-01 | |
-| contextual_hint.tracking_protection.outside_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user tapped outside of the etp contextual hint (which has no effect). |[1](https://github.com/mozilla-mobile/fenix/pull/11923)||2020-10-01 | |
-| crash_reporter.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The crash reporter was closed |[1](https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708)|
crash_submitted: A boolean that tells us whether or not the user submitted a crash report
|2020-10-01 | |
-| crash_reporter.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The crash reporter was displayed |[1](https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708)||2020-10-01 | |
-| custom_tab.action_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the action button provided by the launching app |[1](https://github.com/mozilla-mobile/fenix/pull/1697)||2020-10-01 | |
-| custom_tab.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed the custom tab |[1](https://github.com/mozilla-mobile/fenix/pull/1697)||2020-10-01 | |
-| custom_tab.menu |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the custom tabs menu |[1](https://github.com/mozilla-mobile/fenix/pull/1697)||2020-10-01 | |
-| download_notification.cancel |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user cancelled a download in the download notification |[1](https://github.com/mozilla-mobile/fenix/pull/6554)||2020-10-01 | |
-| download_notification.in_app_open |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a downloaded file in the in-app notification link |[1](https://github.com/mozilla-mobile/fenix/pull/6554)||2020-10-01 | |
-| download_notification.in_app_try_again |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped on try again when a download fails in the in-app notification link |[1](https://github.com/mozilla-mobile/fenix/pull/6554)||2020-10-01 | |
-| download_notification.open |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a downloaded file in the download notification |[1](https://github.com/mozilla-mobile/fenix/pull/6554)||2020-10-01 | |
-| download_notification.pause |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user paused a download in the download notification |[1](https://github.com/mozilla-mobile/fenix/pull/6554)||2020-10-01 | |
-| download_notification.resume |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user resumed a download in the download notification |[1](https://github.com/mozilla-mobile/fenix/pull/6554)||2020-10-01 | |
-| download_notification.try_again |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped on try again when a download fails in the download notification |[1](https://github.com/mozilla-mobile/fenix/pull/6554)||2020-10-01 | |
-| error_page.visited_error |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user encountered an error page |[1](https://github.com/mozilla-mobile/fenix/pull/2491#issuecomment-492414486)|
error_type: The error type of the error page encountered
|2020-10-01 | |
-| events.app_opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the app (from cold start, to the homescreen or browser) |[1](https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673)|
source: The method used to open Fenix. Possible values are: `app_icon`, `custom_tab` or `link`
|2020-10-01 | |
-| events.app_opened_all_startup |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the app to the HomeActivity. The HomeActivity encompasses the home screen, browser screen, settings screen, collections and other screens in the nav_graph. This differs from the app_opened probe because it measures all startups, not just cold startup. Note: There is a short gap between the time application goes into background and the time android reports the application going into the background. Note: This metric does not cover the following cases: Case # 1 -> a). open a link(for example, gmail) with in-app Browser (metric report custom_tab startup) b). press home button c). open gmail again (which brings us back to in app browser). Step c will not report startup metric. Case # 2 -> a). open fenix b). press home button c). launch fenix through app switcher/recent apps. step c will not report startup type. |[1](https://github.com/mozilla-mobile/fenix/pull/12114#pullrequestreview-445245341)|
source: The method used to open Fenix. Possible values are `app_icon`, `custom_tab`, `link` or `unknown`
|2020-12-01 | |
-| events.app_received_intent |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The system received an Intent for the HomeActivity. An intent is received an external entity wants to the app to display content. Intents can be received when the app is closed – at which point the app will be opened – or when the app is already opened – at which point the already open app will make changes such as loading a url. This can be used loosely as a heuristic for when the user requested to open the app. The HomeActivity encompasses the home screen and browser screen but may include other screens. This differs from the app_opened probe because it measures all startups, not just cold startup. |[1](https://github.com/mozilla-mobile/fenix/pull/11940/)|
source: The method used to open Fenix. Possible values are `app_icon`, `custom_tab`, `link` or `unknown`
|2020-12-01 | |
-| events.browser_menu_action |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A browser menu item was tapped |[1](https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708), [2](https://github.com/mozilla-mobile/fenix/pull/5098#issuecomment-529658996), [3](https://github.com/mozilla-mobile/fenix/pull/6310)|
item: A string containing the name of the item the user tapped. These items include: Settings, Help, Desktop Site toggle on/off, Find in Page, New Tab, Private Tab, Share, Report Site Issue, Back/Forward button, Reload Button, Quit, Reader Mode On, Reader Mode Off, Open In app, Add To Top Sites, Add-ons Manager, Bookmarks, History
|2020-10-01 | |
-| events.entered_url |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user entered a url |[1](https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673)|
autocomplete: A boolean that tells us whether the URL was autofilled by an Autocomplete suggestion
|2020-10-01 | |
-| events.opened_link |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a link with Fenix |[1](https://github.com/mozilla-mobile/fenix/pull/5975)|
mode: The mode the link was opened in. Either 'PRIVATE' or 'NORMAL'.
|2020-10-01 | |
-| events.performed_search |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user performed a search |[1](https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673), [2](https://github.com/mozilla-mobile/fenix/pull/1677)|
source: A string that tells us how the user performed the search. Possible values are: * default.action * default.suggestion * shortcut.action * shortcut.suggestion
|2020-10-01 | |
-| events.preference_toggled |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user toggled a boolean preference in settings |[1](https://github.com/mozilla-mobile/fenix/pull/1896), [2](https://github.com/mozilla-mobile/fenix/pull/5704), [3](https://github.com/mozilla-mobile/fenix/pull/5886), [4](https://github.com/mozilla-mobile/fenix/pull/5975), [5](https://github.com/mozilla-mobile/fenix/pull/6352), [6](https://github.com/mozilla-mobile/fenix/pull/6601), [7](https://github.com/mozilla-mobile/fenix/pull/6746)|
enabled: Whether or not the preference is *now* enabled
preference_key: The preference key for the boolean (true/false) preference the user toggled. We currently track: show_search_suggestions, remote_debugging, telemetry, tracking_protection, search_bookmarks, search_browsing_history, show_clipboard_suggestions, show_search_shortcuts, open_links_in_a_private_tab (bug in implementation https://github.com/mozilla-mobile/fenix/issues/7384), pref_key_sync_logins, pref_key_sync_bookmarks, pref_key_sync_history, pref_key_show_voice_search, and pref_key_show_search_suggestions_in_private.
|2020-10-01 | |
-| events.search_bar_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the search bar |[1](https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673)|
source: The view the user was on when they initiated the search (For example: `Home` or `Browser`)
|2020-10-01 | |
-| events.tab_counter_menu_action |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A tab counter menu item was tapped |[1](https://github.com/mozilla-mobile/fenix/pull/11533)|
item: A string containing the name of the item the user tapped. These items are: New tab, New private tab, Close tab
|2020-10-01 | |
-| events.whats_new_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the "what's new" page button |[1](https://github.com/mozilla-mobile/fenix/pull/5090)||2020-10-01 | |
-| find_in_page.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed the find in page UI |[1](https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010)||2020-10-01 | |
-| find_in_page.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the find in page UI |[1](https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010)||2020-10-01 | |
-| find_in_page.searched_page |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user searched the page |[1](https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010)||2020-10-01 | |
-| history.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the history screen |[1](https://github.com/mozilla-mobile/fenix/pull/3940)||2020-10-01 | |
-| history.opened_item |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a history item |[1](https://github.com/mozilla-mobile/fenix/pull/3940)||2020-10-01 | |
-| history.removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed a history item |[1](https://github.com/mozilla-mobile/fenix/pull/3940)||2020-10-01 | |
-| history.removed_all |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed all history items |[1](https://github.com/mozilla-mobile/fenix/pull/3940)||2020-10-01 | |
-| history.shared |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user shared a history item |[1](https://github.com/mozilla-mobile/fenix/pull/3940)||2020-10-01 | |
-| login_dialog.cancelled |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The login dialog prompt was cancelled |[1](https://github.com/mozilla-mobile/fenix/pull/13050)||2021-02-01 | |
-| login_dialog.displayed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The login dialog prompt was displayed |[1](https://github.com/mozilla-mobile/fenix/pull/13050)||2021-02-01 | |
-| login_dialog.never_save |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The login dialog prompt "never save" button was pressed |[1](https://github.com/mozilla-mobile/fenix/pull/13050)||2021-02-01 | |
-| login_dialog.saved |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The login dialog prompt "save" button was pressed |[1](https://github.com/mozilla-mobile/fenix/pull/13050)||2021-02-01 | |
-| logins.copy_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user copied a piece of a login in saved logins |[1](https://github.com/mozilla-mobile/fenix/pull/6352)||2020-10-01 | |
-| logins.delete_saved_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user confirms delete of a saved login |[1](https://github.com/mozilla-mobile/fenix/issues/11208)||2020-10-01 | |
-| logins.open_individual_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user accessed an individual login in saved logins |[1](https://github.com/mozilla-mobile/fenix/pull/6352)||2020-10-01 | |
-| logins.open_login_editor |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user entered the edit screen for an individual saved login |[1](https://github.com/mozilla-mobile/fenix/issues/11208)||2020-10-01 | |
-| logins.open_logins |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user accessed Logins in Settings |[1](https://github.com/mozilla-mobile/fenix/pull/6352)||2020-10-01 | |
-| logins.save_edited_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user saves changes made to an individual login |[1](https://github.com/mozilla-mobile/fenix/issues/11208)||2020-10-01 | |
-| logins.save_logins_setting_changed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user changed their setting for asking to save logins |[1](https://github.com/mozilla-mobile/fenix/pull/7767)|
setting: The new setting for saving logins the user selected. Either `ask_to_save` or `never_save`
|2020-10-01 | |
-| logins.view_password_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user viewed a password in an individual saved login |[1](https://github.com/mozilla-mobile/fenix/pull/6352)||2020-10-01 | |
-| media_notification.pause |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the pause icon on the media notification |[1](https://github.com/mozilla-mobile/fenix/pull/5520)||2020-10-01 | |
-| media_notification.play |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the play icon on the media notification |[1](https://github.com/mozilla-mobile/fenix/pull/5520)||2020-10-01 | |
-| media_state.pause |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |Media playback was paused. |[1](https://github.com/mozilla-mobile/fenix/pull/6463)||2020-10-01 | |
-| media_state.play |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |Media started playing. |[1](https://github.com/mozilla-mobile/fenix/pull/6463)||2020-10-01 | |
-| media_state.stop |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |Media playback has ended. |[1](https://github.com/mozilla-mobile/fenix/pull/6463)||2020-10-01 | |
-| onboarding.finish |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user taps starts browsing and ends the onboarding experience. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-10-01 | |
-| onboarding.fxa_auto_signin |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The onboarding automatic sign in card was tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-10-01 | |
-| onboarding.fxa_manual_signin |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The onboarding manual sign in card was tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-10-01 | |
-| onboarding.pref_toggled_private_browsing |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The private browsing preference was selected from the onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-10-01 | |
-| onboarding.pref_toggled_theme_picker |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The device theme was chosen using the theme picker onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)|
theme: A string that indicates the theme LIGHT, DARK, or FOLLOW DEVICE. Default: FOLLOW DEVICE
|2020-10-01 | |
-| onboarding.pref_toggled_toolbar_position |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The toolbar position preference was chosen from the onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)|
position: A string that indicates the position of the toolbar TOP or BOTTOM. Default: BOTTOM
|2020-10-01 | |
-| onboarding.pref_toggled_tracking_prot |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tracking protection preference was chosen from the onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)|
setting: A string that indicates the Tracking Protection policy STANDARD or STRICT. Default: Toggle ON, STANDARD
|2020-10-01 | |
-| onboarding.privacy_notice |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The onboarding privacy notice card was tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-10-01 | |
-| onboarding.whats_new |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The onboarding What\'s New card was tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-10-01 | |
-| pocket.pocket_top_site_clicked |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user clicked on the trending Pocket top site |[1](https://github.com/mozilla-mobile/fenix/pull/8098)||2020-10-01 | |
-| pocket.pocket_top_site_removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed the trending Pocket top site |[1](https://github.com/mozilla-mobile/fenix/pull/8098)||2020-10-01 | |
-| private_browsing_mode.garbage_icon |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the garbage can icon on the private browsing home page, deleting all private tabs. |[1](https://github.com/mozilla-mobile/fenix/pull/4968)||2020-10-01 | |
-| private_browsing_mode.notification_delete |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the private browsing mode notification's "Delete and Open" button. |[1](https://github.com/mozilla-mobile/fenix/pull/4968)||2020-10-01 | |
-| private_browsing_mode.notification_open |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the private browsing mode notification's "Open" button. |[1](https://github.com/mozilla-mobile/fenix/pull/4968)||2020-10-01 | |
-| private_browsing_mode.notification_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the private browsing mode notification itself. |[1](https://github.com/mozilla-mobile/fenix/pull/4968)||2020-10-01 | |
-| private_browsing_mode.snackbar_undo |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "undo" button in the snackbar that is shown when the garbage icon is tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/4968)||2020-10-01 | |
-| private_browsing_shortcut.cfr_add_shortcut |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "Add shortcut" button when the contextual feature recommender appeared. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-10-01 | |
-| private_browsing_shortcut.cfr_cancel |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "No thanks" button when the contextual feature recommender appeared. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-10-01 | |
-| private_browsing_shortcut.create_shortcut |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "Add private browsing shortcut" button in settings. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-10-01 | |
-| private_browsing_shortcut.pinned_shortcut_priv |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the pinned private shortcut in Android home screen, opening up a new private search. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-10-01 | |
-| private_browsing_shortcut.static_shortcut_priv |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the long-press shortcut "Open new private tab", opening up a new private search. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-10-01 | |
-| private_browsing_shortcut.static_shortcut_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the long-press shortcut "Open new tab", opening up a new search. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-10-01 | |
-| progressive_web_app.background |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user puts the PWA into the background. |[1](https://github.com/mozilla-mobile/fenix/pull/11859)|
time_ms: The current time in ms when the PWA was backgrounded.
|2021-03-01 | |
-| progressive_web_app.foreground |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user brings the PWA into the foreground. |[1](https://github.com/mozilla-mobile/fenix/pull/11859)|
time_ms: The current time in ms when the PWA was brought to the foreground.
|2021-03-01 | |
-| progressive_web_app.homescreen_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user taps on PWA homescreen icon |[1](https://github.com/mozilla-mobile/fenix/pull/11859)||2021-03-01 | |
-| progressive_web_app.install_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user installs a PWA. Could be a shortcut or added to homescreen. |[1](https://github.com/mozilla-mobile/fenix/pull/11859)||2021-03-01 | |
-| qr_scanner.navigation_allowed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped "allow" on the prompt, directing the user to the website scanned |[1](https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967)||2020-10-01 | |
-| qr_scanner.navigation_denied |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped "deny" on the prompt, putting the user back to the scanning view |[1](https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967)||2020-10-01 | |
-| qr_scanner.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the QR scanner |[1](https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967)||2020-10-01 | |
-| qr_scanner.prompt_displayed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user scanned a QR code, causing a confirmation prompt to display asking if they want to navigate to the page |[1](https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967)||2020-10-01 | |
-| reader_mode.appearance |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the appearance button |[1](https://github.com/mozilla-mobile/fenix/pull/3941)||2020-10-01 | |
-| reader_mode.available |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |Reader mode is available for the current page |[1](https://github.com/mozilla-mobile/fenix/pull/3941)||2020-10-01 | |
-| reader_mode.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed reader mode |[1](https://github.com/mozilla-mobile/fenix/pull/4328)||2020-10-01 | |
-| reader_mode.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened reader mode |[1](https://github.com/mozilla-mobile/fenix/pull/3941)||2020-10-01 | |
-| search_shortcuts.selected |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user selected a search shortcut engine to use |[1](https://github.com/mozilla-mobile/fenix/pull/1202#issuecomment-476870449)|
engine: The name of the built-in search engine the user selected as a string
|2020-10-01 | |
-| search_suggestions.enable_in_private |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user enabled receiving search suggestions in private sessions |[1](https://github.com/mozilla-mobile/fenix/pull/6746)||2020-10-01 | |
-| search_widget.new_tab_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed anywhere from the Firefox logo until the start of the microphone icon, opening a new tab search screen. |[1](https://github.com/mozilla-mobile/fenix/pull/4714)||2020-10-01 | |
-| search_widget.voice_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the microphone icon, opening a new voice search screen. |[1](https://github.com/mozilla-mobile/fenix/pull/4714)||2020-10-01 | |
-| search_widget_cfr.add_widget_pressed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user pressed the "add widget" button. |[1](https://github.com/mozilla-mobile/fenix/pull/10958)||2020-10-01 | |
-| search_widget_cfr.canceled |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user dismissed the search widget cfr by tapping outside of the prompt |[1](https://github.com/mozilla-mobile/fenix/pull/10958)||2020-10-01 | |
-| search_widget_cfr.displayed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The search widget cfr was displayed. |[1](https://github.com/mozilla-mobile/fenix/pull/10958)||2020-10-01 | |
-| search_widget_cfr.not_now_pressed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user pressed the "not now" button. |[1](https://github.com/mozilla-mobile/fenix/pull/10958)||2020-10-01 | |
-| sync_account.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed the sync account page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-10-01 | |
-| sync_account.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the sync account page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-10-01 | |
-| sync_account.send_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user sent the current tab to another FxA device |[1](https://github.com/mozilla-mobile/fenix/pull/5106)||2020-10-01 | |
-| sync_account.sign_in_to_send_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "sign in to send tab" button inside the share tab menu |[1](https://github.com/mozilla-mobile/fenix/pull/5106)||2020-10-01 | |
-| sync_account.sync_now |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the sync now button on the sync account page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-10-01 | |
-| sync_auth.auto_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |User signed into FxA via an account shared from another locally installed Mozilla application (e.g. Fennec) |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-10-01 | |
-| sync_auth.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed the sync page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-10-01 | |
-| sync_auth.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the sync authentication page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-10-01 | |
-| sync_auth.other_external |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |User authenticated via FxA using an unknown mechanism. "Known" mechanisms are currently sign-in, sign-up and pairing |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-10-01 | |
-| sync_auth.paired |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |User signed into FxA by pairing with a different Firefox browser, using a QR code |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-10-01 | |
-| sync_auth.recovered |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |Account manager automatically recovered FxA authentication state without direct user involvement |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-10-01 | |
-| sync_auth.scan_pairing |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the scan pairing button on the sync authentication page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-10-01 | |
-| sync_auth.sign_in |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the sign in button on the sync authentication page and was successfully signed in to FxA |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-10-01 | |
-| sync_auth.sign_out |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the sign out button on the sync account page and was successfully signed out of FxA |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-10-01 | |
-| sync_auth.sign_up |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |User registered a new Firefox Account, and was signed into it |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-10-01 | |
-| sync_auth.use_email |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user chose to use their email to sign in instead of scanning a QR code, counterpart to "scan_pairing" |[1](https://github.com/mozilla-mobile/fenix/pull/9835#pullrequestreview-398641844)||2020-10-01 | |
-| sync_auth.use_email_problem |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user chose to use their email to sign in after an account problem |[1](https://github.com/mozilla-mobile/fenix/pull/9835#pullrequestreview-398641844)||2020-10-01 | |
-| tab.media_pause |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the pause icon on a tab from the home screen |[1](https://github.com/mozilla-mobile/fenix/pull/5266)||2020-10-01 | |
-| tab.media_play |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the play icon on a tab from the home screen |[1](https://github.com/mozilla-mobile/fenix/pull/5266)||2020-10-01 | |
-| tabs_tray.close_all_tabs |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the close all tabs button in the three dot menu within the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tabs_tray.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tabs_tray.closed_existing_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed an existing tab |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tabs_tray.menu_opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened three three dot menu in the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tabs_tray.new_private_tab_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a new private tab |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tabs_tray.new_tab_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a new tab |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tabs_tray.normal_mode_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user switched to normal mode |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tabs_tray.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tabs_tray.opened_existing_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened an existing tab |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tabs_tray.private_mode_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user switched to private mode |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tabs_tray.save_to_collection |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the save to collection button in the three dot menu within the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tabs_tray.share_all_tabs |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the share all tabs button in the three dot menu within the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-10-01 | |
-| tip.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tip was closed |[1](https://github.com/mozilla-mobile/fenix/pull/9836)|
identifier: The identifier of the tip closed
|2020-10-01 | |
-| tip.displayed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tip was displayed |[1](https://github.com/mozilla-mobile/fenix/pull/9836)|
identifier: The identifier of the tip the action was taken on
|2020-10-01 | |
-| toolbar_settings.changed_position |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user selected a new position for the toolbar |[1](https://github.com/mozilla-mobile/fenix/pull/6608)|
position: A string that indicates the new position of the toolbar TOP or BOTTOM
|2020-10-01 | |
-| top_sites.open_default |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a default top site |[1](https://github.com/mozilla-mobile/fenix/pull/10752)||2020-10-01 | |
-| top_sites.open_in_new_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opens a new tab based on a top site item |[1](https://github.com/mozilla-mobile/fenix/pull/7523)||2020-10-01 | |
-| top_sites.open_in_private_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opens a new private tab based on a top site item |[1](https://github.com/mozilla-mobile/fenix/pull/7523)||2020-10-01 | |
-| top_sites.remove |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removes a top site item |[1](https://github.com/mozilla-mobile/fenix/pull/7523)||2020-10-01 | |
-| tracking_protection.etp_setting_changed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user changed their tracking protection level setting to either strict, standard, or custom. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188), [2](https://github.com/mozilla-mobile/fenix/pull/11383)|
etp_setting: The new setting for ETP: strict, standard, custom
|2020-10-01 | |
-| tracking_protection.etp_settings |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened tracking protection settings through settings. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-10-01 | |
-| tracking_protection.etp_shield |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the tracking protection shield icon in toolbar. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-10-01 | |
-| tracking_protection.etp_tracker_list |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed into a list of categorized trackers in tracking protection panel. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-10-01 | |
-| tracking_protection.exception_added |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user added a tracking protection exception through the TP toggle in the panel. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-10-01 | |
-| tracking_protection.panel_settings |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened tracking protection settings from the panel. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-10-01 | |
-| user_specified_search_engines.custom_engine_added |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user added a new custom search engine |[1](https://github.com/mozilla-mobile/fenix/pull/6918)||2020-10-01 | |
-| user_specified_search_engines.custom_engine_deleted |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user deleted a custom search engine |[1](https://github.com/mozilla-mobile/fenix/pull/6918)||2020-10-01 | |
-| voice_search.tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user selected the voice search button on the search screen. |[1](https://github.com/mozilla-mobile/fenix/pull/10785)||2020-10-01 | |
+| about_page.privacy_notice_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped on "Privacy notice" item from About page |[1](https://github.com/mozilla-mobile/fenix/pull/8047), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| about_page.support_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped on "Support" item from About page |[1](https://github.com/mozilla-mobile/fenix/pull/8047), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| addons.open_addon_in_toolbar_menu |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user interacted with an installed add-on in the toolbar menu |[1](https://github.com/mozilla-mobile/fenix/pull/8318), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
addon_id: The id of the add-on that was interacted with in the toolbar menu
|2021-04-01 |2 |
+| addons.open_addons_in_settings |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user accessed "Add-ons" from the Settings |[1](https://github.com/mozilla-mobile/fenix/pull/8318), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| app_theme.dark_theme_selected |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user selected Dark Theme |[1](https://github.com/mozilla-mobile/fenix/pull/7968), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
source: The source from where dark theme was selected. The source can be 'SETTINGS' or 'ONBOARDING'
|2020-04-01 |2 |
+| autoplay.setting_changed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user changed their autoplay setting to either block_cellular, block_audio, or block_all. |[1](https://github.com/mozilla-mobile/fenix/pull/13041#issuecomment-665777411)|
autoplay_setting: The new setting for autoplay: block_cellular, block_audio, or block_all.
|2021-02-01 |2 |
+| autoplay.visited_setting |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user visited the autoplay settings screen |[1](https://github.com/mozilla-mobile/fenix/pull/13041#issuecomment-665777411)||2021-02-01 |2 |
+| bookmarks_management.copied |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user copied a bookmark. |[1](https://github.com/mozilla-mobile/fenix/pull/1708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| bookmarks_management.edited |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user edited the title and/or URL of an existing bookmark. |[1](https://github.com/mozilla-mobile/fenix/pull/1708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| bookmarks_management.folder_add |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user added a new bookmark folder. |[1](https://github.com/mozilla-mobile/fenix/pull/1708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| bookmarks_management.folder_remove |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed a bookmark folder. |[1](https://github.com/mozilla-mobile/fenix/pull/3724), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| bookmarks_management.moved |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user moved an existing bookmark or folder to another folder. |[1](https://github.com/mozilla-mobile/fenix/pull/1708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| bookmarks_management.multi_removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed multiple bookmarks at once. |[1](https://github.com/mozilla-mobile/fenix/pull/1708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| bookmarks_management.open_in_new_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a bookmark in a new tab. |[1](https://github.com/mozilla-mobile/fenix/pull/1708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| bookmarks_management.open_in_new_tabs |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened multiple bookmarks at once in new tabs. |[1](https://github.com/mozilla-mobile/fenix/pull/1708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| bookmarks_management.open_in_private_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a bookmark in a new private tab. |[1](https://github.com/mozilla-mobile/fenix/pull/1708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| bookmarks_management.open_in_private_tabs |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened multiple bookmarks at once in new private tabs. |[1](https://github.com/mozilla-mobile/fenix/pull/1708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| bookmarks_management.removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed a bookmark item. |[1](https://github.com/mozilla-mobile/fenix/pull/1708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| bookmarks_management.shared |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user shared a bookmark. |[1](https://github.com/mozilla-mobile/fenix/pull/1708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| collections.add_tab_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the "add tab" button in the three dot menu of collections |[1](https://github.com/mozilla-mobile/fenix/pull/4358), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| collections.all_tabs_restored |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped "open tabs" from collection menu |[1](https://github.com/mozilla-mobile/fenix/pull/3935), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |1, 2 |
+| collections.long_press |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user long pressed on a tab, triggering the collection creation screen |[1](https://github.com/mozilla-mobile/fenix/pull/4358), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| collections.removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped delete collection from collection menu |[1](https://github.com/mozilla-mobile/fenix/pull/3935), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |1, 2 |
+| collections.rename_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "rename collection" button in the three dot menu |[1](https://github.com/mozilla-mobile/fenix/pull/4539), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| collections.renamed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user renamed a collection |[1](https://github.com/mozilla-mobile/fenix/pull/3935), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |1, 2 |
+| collections.save_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "save to collection" button on either the home or browser screen, triggering the collection creation screen to open (tab_select_opened) |[1](https://github.com/mozilla-mobile/fenix/pull/4358), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
from_screen: A string representing the screen from which the user pressed the save button. Currently one of: `browserMenu`, `homeMenu` or `home`
|2021-04-01 |2 |
+| collections.saved |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user saved a list of tabs to a new collection |[1](https://github.com/mozilla-mobile/fenix/pull/3935), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
tabs_open: The number of tabs open in the current session
tabs_selected: The number of tabs added to the collection
|2021-04-01 |1, 2 |
+| collections.shared |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped share collection |[1](https://github.com/mozilla-mobile/fenix/pull/3935), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |1, 2 |
+| collections.tab_removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped remove tab from collection tab list |[1](https://github.com/mozilla-mobile/fenix/pull/3935), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |1, 2 |
+| collections.tab_restored |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user restored a tab from collection tab list |[1](https://github.com/mozilla-mobile/fenix/pull/3935), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |1, 2 |
+| collections.tab_select_opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the select tabs screen (the first step of the collection creation flow) |[1](https://github.com/mozilla-mobile/fenix/pull/3935), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |1, 2 |
+| collections.tabs_added |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user saved a list of tabs to an existing collection |[1](https://github.com/mozilla-mobile/fenix/pull/3935), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
tabs_open: The number of tabs open in the current session
tabs_selected: The number of tabs added to the collection
|2021-04-01 |1, 2 |
+| context_menu.item_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped an item in the browsers context menu |[1](https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
named: The name of the item that was tapped. Available items are: ``` open_in_new_tab, open_in_private_tab, open_image_in_new_tab, save_image, share_link, copy_link, copy_image_location ```
|2021-04-01 |2 |
+| contextual_hint.tracking_protection.dismiss |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The enhanced tracking protection contextual hint was dismissed by pressing the close button |[1](https://github.com/mozilla-mobile/fenix/pull/11923), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| contextual_hint.tracking_protection.display |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The enhanced tracking protection contextual hint was displayed. |[1](https://github.com/mozilla-mobile/fenix/pull/11923), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| contextual_hint.tracking_protection.inside_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user tapped inside of the etp contextual hint (which brings up the etp panel for this site). |[1](https://github.com/mozilla-mobile/fenix/pull/11923), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| contextual_hint.tracking_protection.outside_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user tapped outside of the etp contextual hint (which has no effect). |[1](https://github.com/mozilla-mobile/fenix/pull/11923), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| crash_reporter.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The crash reporter was closed |[1](https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
crash_submitted: A boolean that tells us whether or not the user submitted a crash report
|2021-04-01 |2 |
+| crash_reporter.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The crash reporter was displayed |[1](https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| custom_tab.action_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the action button provided by the launching app |[1](https://github.com/mozilla-mobile/fenix/pull/1697), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| custom_tab.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed the custom tab |[1](https://github.com/mozilla-mobile/fenix/pull/1697), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| custom_tab.menu |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the custom tabs menu |[1](https://github.com/mozilla-mobile/fenix/pull/1697), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| download_notification.cancel |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user cancelled a download in the download notification |[1](https://github.com/mozilla-mobile/fenix/pull/6554), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| download_notification.in_app_open |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a downloaded file in the in-app notification link |[1](https://github.com/mozilla-mobile/fenix/pull/6554), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| download_notification.in_app_try_again |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped on try again when a download fails in the in-app notification link |[1](https://github.com/mozilla-mobile/fenix/pull/6554), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| download_notification.open |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a downloaded file in the download notification |[1](https://github.com/mozilla-mobile/fenix/pull/6554), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| download_notification.pause |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user paused a download in the download notification |[1](https://github.com/mozilla-mobile/fenix/pull/6554), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| download_notification.resume |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user resumed a download in the download notification |[1](https://github.com/mozilla-mobile/fenix/pull/6554), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| download_notification.try_again |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped on try again when a download fails in the download notification |[1](https://github.com/mozilla-mobile/fenix/pull/6554), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| error_page.visited_error |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user encountered an error page |[1](https://github.com/mozilla-mobile/fenix/pull/2491#issuecomment-492414486), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
error_type: The error type of the error page encountered
|2021-04-01 |2 |
+| events.app_opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the app (from cold start, to the homescreen or browser) |[1](https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
source: The method used to open Fenix. Possible values are: `app_icon`, `custom_tab` or `link`
|2021-04-01 |2 |
+| events.app_opened_all_startup |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the app to the HomeActivity. The HomeActivity encompasses the home screen, browser screen, settings screen, collections and other screens in the nav_graph. This differs from the app_opened probe because it measures all startups, not just cold startup. Note: There is a short gap between the time application goes into background and the time android reports the application going into the background. Note: This metric does not record souce when app opened from task switcher: open application -> press home button -> open recent tasks -> choose fenix. In this case will report [source = unknown, type = hot, has_saved_instance_state = false]. |[1](https://github.com/mozilla-mobile/fenix/pull/12114#pullrequestreview-445245341), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877), [3](https://github.com/mozilla-mobile/fenix/pull/13494#pullrequestreview-474050499)|
has_saved_instance_state: boolean value whether or not startup type has a savedInstance. using savedInstance, HomeActivity's previous state can be restored. This is an optional key since it is not applicable to all the cases. for example, when we are doing a hot start up, we cant have a savedInstanceState therefore we report only [APP_ICON, HOT] instead of [APP_ICON, HOT, false].
source: The method used to open Fenix. Possible values are `app_icon`, `custom_tab`, `link` or `unknown`. unknown is for startup sources where we can't pinpoint the cause. One UNKNOWN case is the app switcher where we don't know what variables to check to ensure this startup wasn't caused by something else.
type: the startup type for opening fenix. the application and HomeActivity either needs to be created or started again. possible values are `cold`, `warm`, `hot` or `error`. Error is for impossible cases. Please file a bug if you see the error case. app created AND HomeActivity created = cold app started AND HomeActivity created = warm app started AND HomeActivity started = hot app created AND HomeActivity started = error
|2021-06-01 |2 |
+| events.app_received_intent |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The system received an Intent for the HomeActivity. An intent is received an external entity wants to the app to display content. Intents can be received when the app is closed – at which point the app will be opened – or when the app is already opened – at which point the already open app will make changes such as loading a url. This can be used loosely as a heuristic for when the user requested to open the app. The HomeActivity encompasses the home screen and browser screen but may include other screens. This differs from the app_opened probe because it measures all startups, not just cold startup. |[1](https://github.com/mozilla-mobile/fenix/pull/11940/), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
source: The method used to open Fenix. Possible values are `app_icon`, `custom_tab`, `link` or `unknown`
|2021-06-01 | |
+| events.browser_menu_action |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A browser menu item was tapped |[1](https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708), [2](https://github.com/mozilla-mobile/fenix/pull/5098#issuecomment-529658996), [3](https://github.com/mozilla-mobile/fenix/pull/6310), [4](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
item: A string containing the name of the item the user tapped. These items include: Settings, Help, Desktop Site toggle on/off, Find in Page, New Tab, Private Tab, Share, Report Site Issue, Back/Forward button, Reload Button, Quit, Reader Mode On, Reader Mode Off, Open In app, Add To Top Sites, Add-ons Manager, Bookmarks, History
|2021-04-01 |2 |
+| events.entered_url |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user entered a url |[1](https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
autocomplete: A boolean that tells us whether the URL was autofilled by an Autocomplete suggestion
|2021-04-01 |2 |
+| events.opened_link |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a link with Fenix |[1](https://github.com/mozilla-mobile/fenix/pull/5975), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
mode: The mode the link was opened in. Either 'PRIVATE' or 'NORMAL'. N.B.: this probe may be incorrectly implemented: see https://github.com/mozilla-mobile/fenix/issues/14133
|2021-04-01 |2 |
+| events.performed_search |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user performed a search |[1](https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673), [2](https://github.com/mozilla-mobile/fenix/pull/1677), [3](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
source: A string that tells us how the user performed the search. Possible values are: * default.action * default.suggestion * shortcut.action * shortcut.suggestion
|2021-04-01 |2 |
+| events.preference_toggled |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user toggled a boolean preference in settings |[1](https://github.com/mozilla-mobile/fenix/pull/1896), [2](https://github.com/mozilla-mobile/fenix/pull/5704), [3](https://github.com/mozilla-mobile/fenix/pull/5886), [4](https://github.com/mozilla-mobile/fenix/pull/5975), [5](https://github.com/mozilla-mobile/fenix/pull/6352), [6](https://github.com/mozilla-mobile/fenix/pull/6601), [7](https://github.com/mozilla-mobile/fenix/pull/6746), [8](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
enabled: Whether or not the preference is *now* enabled
preference_key: The preference key for the boolean (true/false) preference the user toggled. We currently track: show_search_suggestions, remote_debugging, telemetry, tracking_protection, search_bookmarks, search_browsing_history, show_clipboard_suggestions, show_search_shortcuts, open_links_in_a_private_tab (bug in implementation https://github.com/mozilla-mobile/fenix/issues/7384), pref_key_sync_logins, pref_key_sync_bookmarks, pref_key_sync_history, pref_key_show_voice_search, and pref_key_show_search_suggestions_in_private.
|2021-06-01 |1, 2 |
+| events.search_bar_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the search bar |[1](https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
source: The view the user was on when they initiated the search (For example: `Home` or `Browser`)
|2021-04-01 |2 |
+| events.tab_counter_menu_action |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A tab counter menu item was tapped |[1](https://github.com/mozilla-mobile/fenix/pull/11533), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)|
item: A string containing the name of the item the user tapped. These items are: New tab, New private tab, Close tab
|2021-04-01 |2 |
+| events.whats_new_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the "what's new" page button |[1](https://github.com/mozilla-mobile/fenix/pull/5090), [2](https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877)||2021-04-01 |2 |
+| find_in_page.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed the find in page UI |[1](https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010)||2020-11-15 |2 |
+| find_in_page.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the find in page UI |[1](https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010)||2020-11-15 |2 |
+| find_in_page.searched_page |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user searched the page |[1](https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010)||2020-11-15 |2 |
+| history.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the history screen |[1](https://github.com/mozilla-mobile/fenix/pull/3940)||2020-11-15 |2 |
+| history.opened_item |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a history item |[1](https://github.com/mozilla-mobile/fenix/pull/3940)||2020-11-15 |2 |
+| history.removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed a history item |[1](https://github.com/mozilla-mobile/fenix/pull/3940)||2020-11-15 |2 |
+| history.removed_all |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed all history items |[1](https://github.com/mozilla-mobile/fenix/pull/3940)||2020-11-15 |2 |
+| history.shared |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user shared a history item |[1](https://github.com/mozilla-mobile/fenix/pull/3940)||2020-11-15 |2 |
+| login_dialog.cancelled |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The login dialog prompt was cancelled |[1](https://github.com/mozilla-mobile/fenix/pull/13050)||2021-02-01 |2 |
+| login_dialog.displayed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The login dialog prompt was displayed |[1](https://github.com/mozilla-mobile/fenix/pull/13050)||2021-02-01 |2 |
+| login_dialog.never_save |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The login dialog prompt "never save" button was pressed |[1](https://github.com/mozilla-mobile/fenix/pull/13050)||2021-02-01 |2 |
+| login_dialog.saved |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The login dialog prompt "save" button was pressed |[1](https://github.com/mozilla-mobile/fenix/pull/13050)||2021-02-01 |2 |
+| logins.copy_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user copied a piece of a login in saved logins |[1](https://github.com/mozilla-mobile/fenix/pull/6352)||2020-11-15 |2 |
+| logins.delete_saved_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user confirms delete of a saved login |[1](https://github.com/mozilla-mobile/fenix/issues/11208)||2020-11-15 |2 |
+| logins.open_individual_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user accessed an individual login in saved logins |[1](https://github.com/mozilla-mobile/fenix/pull/6352)||2020-11-15 |2 |
+| logins.open_login_editor |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user entered the edit screen for an individual saved login |[1](https://github.com/mozilla-mobile/fenix/issues/11208)||2020-11-15 |2 |
+| logins.open_logins |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user accessed Logins in Settings |[1](https://github.com/mozilla-mobile/fenix/pull/6352)||2020-11-15 |2 |
+| logins.save_edited_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user saves changes made to an individual login |[1](https://github.com/mozilla-mobile/fenix/issues/11208)||2020-11-15 |2 |
+| logins.save_logins_setting_changed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user changed their setting for asking to save logins |[1](https://github.com/mozilla-mobile/fenix/pull/7767)|
setting: The new setting for saving logins the user selected. Either `ask_to_save` or `never_save`
|2020-11-15 |2 |
+| logins.view_password_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user viewed a password in an individual saved login |[1](https://github.com/mozilla-mobile/fenix/pull/6352)||2020-11-15 |2 |
+| media_notification.pause |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the pause icon on the media notification |[1](https://github.com/mozilla-mobile/fenix/pull/5520)||2020-11-15 |2 |
+| media_notification.play |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the play icon on the media notification |[1](https://github.com/mozilla-mobile/fenix/pull/5520)||2020-11-15 |2 |
+| media_state.pause |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |Media playback was paused. |[1](https://github.com/mozilla-mobile/fenix/pull/6463)||2020-11-15 |2 |
+| media_state.play |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |Media started playing. |[1](https://github.com/mozilla-mobile/fenix/pull/6463)||2020-11-15 |2 |
+| media_state.stop |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |Media playback has ended. |[1](https://github.com/mozilla-mobile/fenix/pull/6463)||2020-11-15 |2 |
+| onboarding.finish |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user taps starts browsing and ends the onboarding experience. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-11-15 |2 |
+| onboarding.fxa_auto_signin |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The onboarding automatic sign in card was tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-11-15 |2 |
+| onboarding.fxa_manual_signin |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The onboarding manual sign in card was tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-11-15 |2 |
+| onboarding.pref_toggled_private_browsing |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The private browsing preference was selected from the onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-11-15 |2 |
+| onboarding.pref_toggled_theme_picker |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The device theme was chosen using the theme picker onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)|
theme: A string that indicates the theme LIGHT, DARK, or FOLLOW DEVICE. Default: FOLLOW DEVICE
|2020-11-15 |2 |
+| onboarding.pref_toggled_toolbar_position |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The toolbar position preference was chosen from the onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)|
position: A string that indicates the position of the toolbar TOP or BOTTOM. Default: BOTTOM
|2020-11-15 |2 |
+| onboarding.pref_toggled_tracking_prot |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tracking protection preference was chosen from the onboarding card. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)|
setting: A string that indicates the Tracking Protection policy STANDARD or STRICT. Default: Toggle ON, STANDARD
|2020-11-15 |2 |
+| onboarding.privacy_notice |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The onboarding privacy notice card was tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-11-15 |2 |
+| onboarding.whats_new |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The onboarding What\'s New card was tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/11867)||2020-11-15 |2 |
+| pocket.pocket_top_site_clicked |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user clicked on the trending Pocket top site |[1](https://github.com/mozilla-mobile/fenix/pull/8098)||2020-11-15 |2 |
+| pocket.pocket_top_site_removed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removed the trending Pocket top site |[1](https://github.com/mozilla-mobile/fenix/pull/8098)||2020-11-15 |2 |
+| private_browsing_mode.garbage_icon |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the garbage can icon on the private browsing home page, deleting all private tabs. |[1](https://github.com/mozilla-mobile/fenix/pull/4968)||2020-11-15 |2 |
+| private_browsing_mode.notification_delete |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the private browsing mode notification's "Delete and Open" button. |[1](https://github.com/mozilla-mobile/fenix/pull/4968)||2020-11-15 |2 |
+| private_browsing_mode.notification_open |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the private browsing mode notification's "Open" button. |[1](https://github.com/mozilla-mobile/fenix/pull/4968)||2020-11-15 |2 |
+| private_browsing_mode.notification_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the private browsing mode notification itself. |[1](https://github.com/mozilla-mobile/fenix/pull/4968)||2020-11-15 |2 |
+| private_browsing_mode.snackbar_undo |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "undo" button in the snackbar that is shown when the garbage icon is tapped. |[1](https://github.com/mozilla-mobile/fenix/pull/4968)||2020-11-15 |2 |
+| private_browsing_shortcut.cfr_add_shortcut |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "Add shortcut" button when the contextual feature recommender appeared. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-11-15 |2 |
+| private_browsing_shortcut.cfr_cancel |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "No thanks" button when the contextual feature recommender appeared. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-11-15 |2 |
+| private_browsing_shortcut.create_shortcut |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "Add private browsing shortcut" button in settings. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-11-15 |2 |
+| private_browsing_shortcut.pinned_shortcut_priv |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the pinned private shortcut in Android home screen, opening up a new private search. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-11-15 |2 |
+| private_browsing_shortcut.static_shortcut_priv |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the long-press shortcut "Open new private tab", opening up a new private search. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-11-15 |2 |
+| private_browsing_shortcut.static_shortcut_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the long-press shortcut "Open new tab", opening up a new search. |[1](https://github.com/mozilla-mobile/fenix/pull/5194)||2020-11-15 |2 |
+| progressive_web_app.background |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user puts the PWA into the background. |[1](https://github.com/mozilla-mobile/fenix/pull/11859)|
time_ms: The current time in ms when the PWA was backgrounded.
|2021-03-01 |2 |
+| progressive_web_app.foreground |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user brings the PWA into the foreground. |[1](https://github.com/mozilla-mobile/fenix/pull/11859)|
time_ms: The current time in ms when the PWA was brought to the foreground.
|2021-03-01 |2 |
+| progressive_web_app.homescreen_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user taps on PWA homescreen icon |[1](https://github.com/mozilla-mobile/fenix/pull/11859)||2021-03-01 |2 |
+| progressive_web_app.install_tap |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user installs a PWA. Could be a shortcut or added to homescreen. |[1](https://github.com/mozilla-mobile/fenix/pull/11859)||2021-03-01 |2 |
+| qr_scanner.navigation_allowed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped "allow" on the prompt, directing the user to the website scanned |[1](https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967)||2020-11-15 |2 |
+| qr_scanner.navigation_denied |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped "deny" on the prompt, putting the user back to the scanning view |[1](https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967)||2020-11-15 |2 |
+| qr_scanner.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the QR scanner |[1](https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967)||2020-11-15 |2 |
+| qr_scanner.prompt_displayed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user scanned a QR code, causing a confirmation prompt to display asking if they want to navigate to the page |[1](https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967)||2020-11-15 |2 |
+| reader_mode.appearance |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the appearance button |[1](https://github.com/mozilla-mobile/fenix/pull/3941)||2020-11-15 |2 |
+| reader_mode.available |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |Reader mode is available for the current page |[1](https://github.com/mozilla-mobile/fenix/pull/3941)||2020-11-15 |2 |
+| reader_mode.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed reader mode |[1](https://github.com/mozilla-mobile/fenix/pull/4328)||2020-11-15 |2 |
+| reader_mode.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened reader mode |[1](https://github.com/mozilla-mobile/fenix/pull/3941)||2020-11-15 |2 |
+| search_shortcuts.selected |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user selected a search shortcut engine to use |[1](https://github.com/mozilla-mobile/fenix/pull/1202#issuecomment-476870449)|
engine: The name of the built-in search engine the user selected as a string
|2020-11-15 | |
+| search_suggestions.enable_in_private |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user enabled receiving search suggestions in private sessions |[1](https://github.com/mozilla-mobile/fenix/pull/6746)||2020-11-15 |1, 2 |
+| search_widget.new_tab_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed anywhere from the Firefox logo until the start of the microphone icon, opening a new tab search screen. |[1](https://github.com/mozilla-mobile/fenix/pull/4714)||2020-11-15 |2 |
+| search_widget.voice_button |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the microphone icon, opening a new voice search screen. |[1](https://github.com/mozilla-mobile/fenix/pull/4714)||2020-11-15 |2 |
+| search_widget_cfr.add_widget_pressed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user pressed the "add widget" button. |[1](https://github.com/mozilla-mobile/fenix/pull/10958)||2020-11-15 |2 |
+| search_widget_cfr.canceled |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user dismissed the search widget cfr by tapping outside of the prompt |[1](https://github.com/mozilla-mobile/fenix/pull/10958)||2020-11-15 |2 |
+| search_widget_cfr.displayed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The search widget cfr was displayed. |[1](https://github.com/mozilla-mobile/fenix/pull/10958)||2020-11-15 |2 |
+| search_widget_cfr.not_now_pressed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user pressed the "not now" button. |[1](https://github.com/mozilla-mobile/fenix/pull/10958)||2020-11-15 |2 |
+| sync_account.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed the sync account page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-11-15 |2 |
+| sync_account.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the sync account page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-11-15 |2 |
+| sync_account.send_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user sent the current tab to another FxA device |[1](https://github.com/mozilla-mobile/fenix/pull/5106)||2020-11-15 |2 |
+| sync_account.sign_in_to_send_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the "sign in to send tab" button inside the share tab menu |[1](https://github.com/mozilla-mobile/fenix/pull/5106)||2020-11-15 |2 |
+| sync_account.sync_now |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the sync now button on the sync account page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-11-15 |2 |
+| sync_auth.auto_login |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |User signed into FxA via an account shared from another locally installed Mozilla application (e.g. Fennec) |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-11-15 |1, 2 |
+| sync_auth.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed the sync page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-11-15 |2 |
+| sync_auth.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the sync authentication page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-11-15 |2 |
+| sync_auth.other_external |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |User authenticated via FxA using an unknown mechanism. "Known" mechanisms are currently sign-in, sign-up and pairing |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-11-15 |1, 2 |
+| sync_auth.paired |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |User signed into FxA by pairing with a different Firefox browser, using a QR code |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-11-15 |1, 2 |
+| sync_auth.recovered |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |Account manager automatically recovered FxA authentication state without direct user involvement |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-11-15 |1, 2 |
+| sync_auth.scan_pairing |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the scan pairing button on the sync authentication page |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-11-15 |2 |
+| sync_auth.sign_in |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the sign in button on the sync authentication page and was successfully signed in to FxA |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-11-15 |2 |
+| sync_auth.sign_out |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the sign out button on the sync account page and was successfully signed out of FxA |[1](https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532)||2020-11-15 |2 |
+| sync_auth.sign_up |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |User registered a new Firefox Account, and was signed into it |[1](https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300)||2020-11-15 |1, 2 |
+| sync_auth.use_email |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user chose to use their email to sign in instead of scanning a QR code, counterpart to "scan_pairing" |[1](https://github.com/mozilla-mobile/fenix/pull/9835#pullrequestreview-398641844)||2020-11-15 |2 |
+| sync_auth.use_email_problem |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user chose to use their email to sign in after an account problem |[1](https://github.com/mozilla-mobile/fenix/pull/9835#pullrequestreview-398641844)||2020-11-15 |2 |
+| tab.media_pause |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the pause icon on a tab from the home screen |[1](https://github.com/mozilla-mobile/fenix/pull/5266)||2020-11-15 |2 |
+| tab.media_play |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the play icon on a tab from the home screen |[1](https://github.com/mozilla-mobile/fenix/pull/5266)||2020-11-15 |2 |
+| tabs_tray.close_all_tabs |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the close all tabs button in the three dot menu within the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tabs_tray.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tabs_tray.closed_existing_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user closed an existing tab |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tabs_tray.menu_opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened three three dot menu in the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tabs_tray.new_private_tab_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a new private tab |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tabs_tray.new_tab_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a new tab |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tabs_tray.normal_mode_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user switched to normal mode |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tabs_tray.opened |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tabs_tray.opened_existing_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened an existing tab |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tabs_tray.private_mode_tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user switched to private mode |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tabs_tray.save_to_collection |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the save to collection button in the three dot menu within the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tabs_tray.share_all_tabs |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user tapped the share all tabs button in the three dot menu within the tabs tray |[1](https://github.com/mozilla-mobile/fenix/pull/12036)||2020-11-15 |2 |
+| tip.closed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tip was closed |[1](https://github.com/mozilla-mobile/fenix/pull/9836)|
identifier: The identifier of the tip closed
|2020-11-15 |2 |
+| tip.displayed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The tip was displayed |[1](https://github.com/mozilla-mobile/fenix/pull/9836)|
identifier: The identifier of the tip the action was taken on
|2020-11-15 |2 |
+| toolbar_settings.changed_position |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |The user selected a new position for the toolbar |[1](https://github.com/mozilla-mobile/fenix/pull/6608)|
position: A string that indicates the new position of the toolbar TOP or BOTTOM
|2020-11-15 |2 |
+| top_sites.open_default |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened a default top site |[1](https://github.com/mozilla-mobile/fenix/pull/10752)||2020-11-15 |2 |
+| top_sites.open_in_new_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opens a new tab based on a top site item |[1](https://github.com/mozilla-mobile/fenix/pull/7523)||2020-11-15 |2 |
+| top_sites.open_in_private_tab |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opens a new private tab based on a top site item |[1](https://github.com/mozilla-mobile/fenix/pull/7523)||2020-11-15 |2 |
+| top_sites.remove |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user removes a top site item |[1](https://github.com/mozilla-mobile/fenix/pull/7523)||2020-11-15 |2 |
+| tracking_protection.etp_setting_changed |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user changed their tracking protection level setting to either strict, standard, or custom. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188), [2](https://github.com/mozilla-mobile/fenix/pull/11383)|
etp_setting: The new setting for ETP: strict, standard, custom
|2020-11-15 |2 |
+| tracking_protection.etp_settings |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened tracking protection settings through settings. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-11-15 |2 |
+| tracking_protection.etp_shield |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed the tracking protection shield icon in toolbar. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-11-15 |2 |
+| tracking_protection.etp_tracker_list |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user pressed into a list of categorized trackers in tracking protection panel. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-11-15 |2 |
+| tracking_protection.exception_added |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user added a tracking protection exception through the TP toggle in the panel. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-11-15 |2 |
+| tracking_protection.panel_settings |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user opened tracking protection settings from the panel. |[1](https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188)||2020-11-15 |2 |
+| user_specified_search_engines.custom_engine_added |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user added a new custom search engine |[1](https://github.com/mozilla-mobile/fenix/pull/6918)||2020-11-15 |2 |
+| user_specified_search_engines.custom_engine_deleted |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user deleted a custom search engine |[1](https://github.com/mozilla-mobile/fenix/pull/6918)||2020-11-15 |2 |
+| voice_search.tapped |[event](https://mozilla.github.io/glean/book/user/metrics/event.html) |A user selected the voice search button on the search screen. |[1](https://github.com/mozilla-mobile/fenix/pull/10785)||2020-11-15 |2 |
## first-session
@@ -248,11 +245,11 @@ The following metrics are added to the ping:
| Name | Type | Description | Data reviews | Extras | Expiration | [Data Sensitivity](https://wiki.mozilla.org/Firefox/Data_Collection) |
| --- | --- | --- | --- | --- | --- | --- |
-| first_session.adgroup |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The name of the AdGroup that was used to source this installation. |[1](https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586480836)||2020-10-01 | |
-| first_session.campaign |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The name of the campaign that is responsible for this installation. |[1](https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202)||2020-10-01 | |
-| first_session.creative |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The identifier of the creative material that the user interacted with. |[1](https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202)||2020-10-01 | |
-| first_session.network |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The name of the Network that sourced this installation. |[1](https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202)||2020-10-01 | |
-| first_session.timestamp |[datetime](https://mozilla.github.io/glean/book/user/metrics/datetime.html) |The Glean generated date and time of the installation. This is unique per app install, though the rest of the data in this ping is from Adjust and will remain static across installs. |[1](https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202)||2020-10-01 | |
+| first_session.adgroup |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The name of the AdGroup that was used to source this installation. |[1](https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586480836)||2020-11-15 |1, 2 |
+| first_session.campaign |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The name of the campaign that is responsible for this installation. |[1](https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202)||2020-11-15 |1, 2 |
+| first_session.creative |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The identifier of the creative material that the user interacted with. |[1](https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202)||2020-11-15 |1, 2 |
+| first_session.network |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The name of the Network that sourced this installation. |[1](https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202)||2020-11-15 |1, 2 |
+| first_session.timestamp |[datetime](https://mozilla.github.io/glean/book/user/metrics/datetime.html) |The Glean generated date and time of the installation. This is unique per app install, though the rest of the data in this ping is from Adjust and will remain static across installs. |[1](https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202)||2020-11-15 |1, 2 |
## metrics
@@ -264,61 +261,61 @@ The following metrics are added to the ping:
| Name | Type | Description | Data reviews | Extras | Expiration | [Data Sensitivity](https://wiki.mozilla.org/Firefox/Data_Collection) |
| --- | --- | --- | --- | --- | --- | --- |
-| addons.enabled_addons |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |A list of all enabled add-ons on the device. |[1](https://github.com/mozilla-mobile/fenix/pull/11080)||2020-10-01 | |
-| addons.has_enabled_addons |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has enabled add-ons on the device. |[1](https://github.com/mozilla-mobile/fenix/pull/8318)||2020-10-01 | |
-| addons.has_installed_addons |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has installed add-ons on the device. |[1](https://github.com/mozilla-mobile/fenix/pull/8318)||2020-10-01 | |
-| addons.installed_addons |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |A list of all installed add-ons on the device. |[1](https://github.com/mozilla-mobile/fenix/pull/11080)||2020-10-01 | |
-| browser.search.ad_clicks |[labeled_counter](https://mozilla.github.io/glean/book/user/metrics/labeled_counters.html) |Records clicks of adverts on SERP pages. The key format is ‘’. |[1](https://github.com/mozilla-mobile/fenix/pull/10112)||2020-10-01 | |
-| browser.search.in_content |[labeled_counter](https://mozilla.github.io/glean/book/user/metrics/labeled_counters.html) |Records the type of interaction a user has on SERP pages. |[1](https://github.com/mozilla-mobile/fenix/pull/10167)||2020-10-01 | |
-| browser.search.with_ads |[labeled_counter](https://mozilla.github.io/glean/book/user/metrics/labeled_counters.html) |Records counts of SERP pages with adverts displayed. The key format is ‘’. |[1](https://github.com/mozilla-mobile/fenix/pull/10112)||2020-10-01 | |
-| events.total_uri_count |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |A counter of URIs visited by the user in the current session, including page reloads. This does not include background page requests and URIs from embedded pages or private browsing but may be incremented without user interaction by website scripts that programmatically redirect to a new location. |[1](https://github.com/mozilla-mobile/fenix/pull/1785), [2](https://github.com/mozilla-mobile/fenix/pull/8314)||2020-10-01 | |
-| metrics.adjust_ad_group |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |A string containing the Adjust ad group ID from which the user installed Fenix. This will not send on the first session the user runs. If the install is organic, this will be empty. |[1](https://github.com/mozilla-mobile/fenix/pull/9253)||2020-10-01 | |
-| metrics.adjust_campaign |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |A string containing the Adjust campaign ID from which the user installed Fenix. This will not send on the first session the user runs. If the install is organic, this will be empty. |[1](https://github.com/mozilla-mobile/fenix/pull/5579)||2020-10-01 | |
-| metrics.adjust_creative |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |A string containing the Adjust creative ID from which the user installed Fenix. This will not send on the first session the user runs. If the install is organic, this will be empty. |[1](https://github.com/mozilla-mobile/fenix/pull/9253)||2020-10-01 | |
-| metrics.adjust_network |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |A string containing the Adjust network ID from which the user installed Fenix. This will not send on the first session the user runs. If the install is organic, this will be empty. |[1](https://github.com/mozilla-mobile/fenix/pull/9253)||2020-10-01 | |
-| metrics.default_browser |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Is Fenix the default browser? |[1](https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673)||2020-10-01 | |
-| metrics.default_moz_browser |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The name of the default browser on device if and only if it's a Mozilla owned product |[1](https://github.com/mozilla-mobile/fenix/pull/1953/), [2](https://github.com/mozilla-mobile/fenix/pull/5216)||2020-10-01 | |
-| metrics.has_open_tabs |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |A boolean that indicates if the user has any open NORMAL tabs. |[1](https://github.com/mozilla-mobile/fenix/pull/12024)||2020-10-01 | |
-| metrics.has_recent_pwas |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |A boolean that indicates if the user has recently used PWAs. See recently_used_pwa_count for the actual count. |[1](https://github.com/mozilla-mobile/fenix/pull/11982#pullrequestreview-437963817)||2020-12-01 | |
-| metrics.has_top_sites |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |A boolean that indicates if the user has top sites |[1](https://github.com/mozilla-mobile/fenix/pull/9556)||2020-10-01 | |
-| metrics.mozilla_products |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |A list of all the Mozilla products installed on device. We currently scan for: Firefox, Firefox Beta, Firefox Aurora, Firefox Nightly, Firefox Fdroid, Firefox Lite, Reference Browser, Reference Browser Debug, Fenix, Focus, and Lockwise. |[1](https://github.com/mozilla-mobile/fenix/pull/1953/), [2](https://github.com/mozilla-mobile/fenix/pull/5216)||2020-10-01 | |
-| metrics.recently_used_pwa_count |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |A counter that indicates how many PWAs a user has recently used. Threshold for "recency" set in HomeActivity#PWA_RECENTLY_USED_THRESHOLD. Currently we are not told by the OS when a PWA is removed by the user, so we use the "recently used" heuristic to judge how many PWAs are still active, as a proxy for "installed". This value will only be set if the user has at least *one* recently used PWA. If they have 0, this metric will not be sent, resulting in a null value during analysis on the server-side. To disambiguate between a failed `recently_used_pwa_count` metric and 0 recent PWAs, please see `has_recent_pwas`. |[1](https://github.com/mozilla-mobile/fenix/pull/11982#pullrequestreview-437963817)||2020-12-01 | |
-| metrics.search_count |[labeled_counter](https://mozilla.github.io/glean/book/user/metrics/labeled_counters.html) |The labels for this counter are `.