diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c31179fb4..53a6f30c2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -50,4 +50,8 @@ # Possible regressions throughout the app *.pro @mozilla-mobile/Performance *proguard* @mozilla-mobile/Performance + +# This file configures perf tests via Jetpack Benchmark. +app/benchmark.gradle @mozilla-mobile/Performance + # --- PERFORMANCE END --- # diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md deleted file mode 100644 index e27d73793..000000000 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: "\U0001F41E Bug report" -about: Create a report to help us improve -title: "[Bug]" -labels: "\U0001F41E bug" -assignees: '' - ---- -[comment]: # (Please do your best to search for duplicate issues before filing a new issue so we can keep our issue board clean) -[comment]: # (Every issue should have the exact bug and steps to reproduce described in it. Please do not file feedback list tickets as it is difficult to parse them and address their individual points) -[comment]: # (Read https://github.com/mozilla-mobile/fenix#i-want-to-file-an-issue for more information) - -## Steps to reproduce - -### Expected behavior - -### Actual behavior - -### Device information - -* Device vendor / model and Android version: ? -* Firefox for Android version: ? (go to Settings -> About Firefox) - diff --git a/.github/ISSUE_TEMPLATE/---bug-report.yml b/.github/ISSUE_TEMPLATE/---bug-report.yml new file mode 100644 index 000000000..f47c4455a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---bug-report.yml @@ -0,0 +1,91 @@ +name: "\U0001F41E Bug report" +description: Create a report to help us improve. +title: "[Bug]: " +labels: ["\U0001F41E bug", "needs:triage"] +body: + - type: markdown + attributes: + value: | + - Please do your best to search for duplicate issues before filing a new issue so we can keep our issue board clean. + - Have a look at ["I want to file an issue!"][info] for more information. + - Read the [Community Participation Guidelines][guidelines] and the [Bugzilla Etiquette guidelines][bugzilla] before filing an issue. + + [info]: https://github.com/mozilla-mobile/fenix#i-want-to-file-an-issue + [guidelines]: https://www.mozilla.org/en-US/about/governance/policies/participation/ + [bugzilla]: https://bugzilla.mozilla.org/page.cgi?id=etiquette.html + - type: textarea + attributes: + label: Steps to reproduce + description: Steps to reproduce the behaviour. + placeholder: | + 1. Have a tab open.. + 2. Go to.. + 3. Click on.. + 4. Observe.. + validations: + required: true + - type: textarea + attributes: + label: Expected behaviour + placeholder: A menu should open.. + validations: + required: true + - type: textarea + attributes: + label: Actual behaviour + placeholder: The app closes unexpectedly.. + validations: + required: true + - type: markdown + attributes: + value: | + # Device information + - type: input + attributes: + label: Device name + description: The name of the device model and manufacturer. + placeholder: Google Pixel 2 + validations: + required: false + - type: input + attributes: + label: Android version + description: You can find the Android version information in the About section of your device's system settings. + placeholder: Android 10 + validations: + required: true + - type: dropdown + attributes: + label: Firefox release type + description: You can find this information in Settings -> About Firefox. + options: + - Firefox Nightly + - Firefox Beta + - Firefox + validations: + required: true + - type: input + attributes: + label: Firefox version + description: You can find this information in Settings -> About Firefox. + placeholder: 89.0.10 + validations: + required: true + - type: textarea + attributes: + label: Device logs + description: | + Device logs or crash information can greatly aid in debugging. You can find some details here on how to [retrieve device logs or crash IDs][log]. + + [log]: https://github.com/mozilla-mobile/fenix/wiki/Logging-Crash-Information + validations: + required: false + - type: textarea + attributes: + label: Additional information + description: | + If you have any additional information for us, use the field below. + Please note, you can attach screenshots or screen recordings here, by + dragging and dropping files in the field below. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/release_checklist.md b/.github/ISSUE_TEMPLATE/release_checklist.md index 6a54d5bca..c94eb075a 100644 --- a/.github/ISSUE_TEMPLATE/release_checklist.md +++ b/.github/ISSUE_TEMPLATE/release_checklist.md @@ -28,7 +28,7 @@ There are two releases this covers: the current sprint that is going out to Beta ## Sprint X.1 End [Wednesday, 2nd week] Cutting a Beta - [ ] Make a new Beta - - [ ] Create a branch off of master (DO NOT PUSH YET) for the *current* milestone of format `releases_v85.0.0`. After that, anything landing in master will be part of the next release. + - [ ] Create a branch off of main (DO NOT PUSH YET) for the *current* milestone of format `releases_v85.0.0`. After that, anything landing in main will be part of the next release. - ⚠️ Please **do not** use `/` in branch names anymore: Taskcluster silently ignores them and doesn't output tasks at the right index. - [ ] Bump `version.txt` to match the new version number - [ ] Grant [mozilla-release-automation-bot](https://github.com/mozilla-release-automation-bot) write access to this branch. @@ -41,7 +41,7 @@ There are two releases this covers: the current sprint that is going out to Beta - [ ] Send an email to QA at mozilla-mobile-qa@softvision.com with a link to the Taskcluster build (subdirectory of the [Fenix CI](https://firefox-ci-tc.services.mozilla.com/tasks/index/mobile.v2.fenix.beta)) ### Bugfix uplifts / Beta Product Integrity (Beta Release until PI green signoff) -- [ ] If bugs are considered release blocker then find someone to fix them on master and the milestone branch (cherry-pick / uplift) +- [ ] If bugs are considered release blocker then find someone to fix them on main and the milestone branch (cherry-pick / uplift) - [ ] Add the uplift request to the appropriate row in the [Uplifts document](https://docs.google.com/spreadsheets/d/1qIvHpcQ3BqJtlzV5T4M1MhbWVxkNiG-ToeYnWEBW4-I/edit#gid=0). - [ ] If needed, ship a new beta version (e.g. v1.0-beta.2) and follow the submission checklist again. - [ ] Once there is GREEN QA signoff, file a [release management bugzilla for rollout](https://bugzilla.mozilla.org/show_bug.cgi?id=1664366) @@ -49,8 +49,8 @@ There are two releases this covers: the current sprint that is going out to Beta ### Uplifting L10N strings to Beta [Wednesday, 2 weeks after sprint end] - [ ] Find the issue ([example](https://github.com/mozilla-mobile/fenix/issues/16381)) filed by L10N / delphine saying string are ready for uplift (it takes 2 weeks for localizers to prepare localization). -- [ ] If there are new locales that are ready to be added to Release, add them to [l10n-release.toml](https://github.com/mozilla-mobile/fenix/blob/master/l10n-release.toml) -- [ ] Run the [L10N uplift script](https://github.com/mozilla-mobile/fenix/blob/master/l10n-uplift.py) against the releases/vX.1 branch (releases/v85.0.0). There will likely be conflicts, but if you are confused, they should match the strings in [main/Nightly](https://github.com/mozilla-mobile/fenix/tree/master/app/src/main/res) +- [ ] If there are new locales that are ready to be added to Release, add them to [l10n-release.toml](https://github.com/mozilla-mobile/fenix/blob/main/l10n-release.toml) +- [ ] Run the [L10N uplift script](https://github.com/mozilla-mobile/fenix/blob/main/l10n-uplift.py) against the releases/vX.1 branch (releases/v85.0.0). There will likely be conflicts, but if you are confused, they should match the strings in [main/Nightly](https://github.com/mozilla-mobile/fenix/tree/main/app/src/main/res) - [ ] Once all conflicts are resolved, tag a new Beta to be released. - [ ] Notify delphine in the L10N issue that the strings have been uplifted, and string quarantine can be lifted diff --git a/.github/stale.yml b/.github/stale.yml index 64e7e858f..cd1131787 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -14,6 +14,7 @@ onlyLabels: [] exemptLabels: - pin - "feature request 🌟" + - "eng:disabled-test" # Set to true to ignore issues in a project (defaults to false) exemptProjects: false diff --git a/.github/workflows/build-contributor-pr.yml b/.github/workflows/build-contributor-pr.yml index 4f09120a9..f7f992f75 100644 --- a/.github/workflows/build-contributor-pr.yml +++ b/.github/workflows/build-contributor-pr.yml @@ -10,7 +10,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: "Clean & Assemble Debug" uses: eskatos/gradle-command-action@v1 with: @@ -29,7 +29,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: "Test Debug Unit Tests" uses: eskatos/gradle-command-action@v1 with: @@ -48,7 +48,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: "Detekt" uses: eskatos/gradle-command-action@v1 with: @@ -72,7 +72,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: "Ktlint" uses: eskatos/gradle-command-action@v1 with: @@ -91,7 +91,7 @@ jobs: - name: Setup Java uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 11 - name: "Lint Debug" uses: eskatos/gradle-command-action@v1 with: @@ -106,7 +106,7 @@ jobs: path: app/build/reports/lint-results-debug.html run-ui: - runs-on: macos-latest + runs-on: macos-11 if: github.event.pull_request.head.repo.full_name != github.repository && github.actor != 'MickeyMoz' timeout-minutes: 60 @@ -125,7 +125,7 @@ jobs: arch: x86_64 profile: pixel_3a script: - "./gradlew connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=\ + "JAVA_HOME=$JAVA_HOME_11_X64 && ./gradlew connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=\ org.mozilla.fenix.ui.NavigationToolbarTest#visitURLTest" - name: Upload Test Artifacts uses: actions/upload-artifact@v2 diff --git a/.github/workflows/sync-strings.yml b/.github/workflows/sync-strings.yml index 6578c36a1..6c8b270f2 100644 --- a/.github/workflows/sync-strings.yml +++ b/.github/workflows/sync-strings.yml @@ -15,12 +15,15 @@ jobs: steps: - name: "Discover Fenix Beta Version" id: fenix-beta-version - uses: mozilla-mobile/fenix-beta-version@1.1.0 + uses: mozilla-mobile/fenix-beta-version@2.0.0 + - name: "Skip non-beta versions" + uses: andymckay/cancel-action@0.2 + if: ${{ steps.fenix-beta-version.outputs.fenix-beta-version == '' }} - name: "Checkout Master Branch" uses: actions/checkout@v2 with: path: main - ref: master + ref: main - name: "Checkout Beta Branch" uses: actions/checkout@v2 with: @@ -37,5 +40,6 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} path: beta branch: automation/sync-strings-${{ steps.fenix-beta-version.outputs.major-beta-version }} - title: "Sync Strings from master to releases_${{steps.fenix-beta-version.outputs.fenix-beta-version}}.0" - body: "This (automated) PR syncs strings from `master` to `releases_${{steps.fenix-beta-version.outputs.fenix-beta-version}}.0.0`" + title: "Sync Strings from main to releases_${{steps.fenix-beta-version.outputs.fenix-beta-version}}.0" + body: "This (automated) PR syncs strings from `main` to `releases_${{steps.fenix-beta-version.outputs.fenix-beta-version}}.0.0`" + labels: needs:review diff --git a/.mergify.yml b/.mergify.yml index 21382057c..befe49c2f 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -86,3 +86,27 @@ pull_request_rules: strict: smart delete_head_branch: force: false + - name: Needs landing - Rebase + conditions: + - check-success=pr-complete + - label=pr:needs-landing + - "#approved-reviews-by>=1" + - -draft + - label!=pr:work-in-progress + - label!=pr:do-not-land + actions: + merge: + method: rebase + strict: smart + - name: Needs landing - Squash + conditions: + - check-success=pr-complete + - label=pr:needs-landing-squashed + - "#approved-reviews-by>=1" + - -draft + - label!=pr:work-in-progress + - label!=pr:do-not-land + actions: + merge: + method: squash + strict: smart diff --git a/.taskcluster.yml b/.taskcluster.yml index 2e71a5c7b..9b647d4e7 100644 --- a/.taskcluster.yml +++ b/.taskcluster.yml @@ -8,7 +8,7 @@ tasks: - $let: taskgraph: branch: taskgraph - revision: a458418ef7cdd6778f1283926c6116966255bc24 + revision: 9daff451cfbe82c5c70237a7b3dbcf4fd3238299 trustDomain: mobile in: $let: @@ -243,7 +243,7 @@ tasks: # Note: This task is built server side without the context or tooling that # exist in tree so we must hard code the hash image: - mozillareleases/taskgraph:decision-mobile-682fbaa1ef17e70ddfe3457da3eaf8e776c4a20fe5bfbdbeba0641fd5bceae2a@sha256:bbb2613aaab79d17e590fbd78c072d0643be40fd1237195703f84280ecc3b302 + mozillareleases/taskgraph:decision-mobile-44b6b7b4c370220eff56efa8b508aa5157ef9c6e74847c7ecc19d640946ba49e@sha256:4107cbc5e154502529e4d38efa4dc89c05ee54e2cbc6e2e66023e68407502894 maxRunTime: 1800 @@ -260,14 +260,14 @@ tasks: in: $if: 'tasks_for == "action"' then: > - PIP_IGNORE_INSTALLED=0 pip install --user /builds/worker/checkouts/taskgraph && - PIP_IGNORE_INSTALLED=0 pip install --user mozilla-version && + PIP_IGNORE_INSTALLED=0 pip3 install --user /builds/worker/checkouts/taskgraph && + PIP_IGNORE_INSTALLED=0 pip3 install --user mozilla-version && taskcluster/scripts/decision-install-sdk.sh && ln -s /builds/worker/artifacts artifacts && ~/.local/bin/taskgraph action-callback else: > - PIP_IGNORE_INSTALLED=0 pip install --user /builds/worker/checkouts/taskgraph && - PIP_IGNORE_INSTALLED=0 pip install --user mozilla-version && + PIP_IGNORE_INSTALLED=0 pip3 install --user /builds/worker/checkouts/taskgraph && + PIP_IGNORE_INSTALLED=0 pip3 install --user mozilla-version && taskcluster/scripts/decision-install-sdk.sh && ln -s /builds/worker/artifacts artifacts && ~/.local/bin/taskgraph decision diff --git a/Jenkinsfile b/Jenkinsfile index 3d0874105..ebfc2e4e6 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,7 @@ pipeline { agent any triggers { - cron(env.BRANCH_NAME == 'master' ? 'H 0 * * *' : '') + cron(env.BRANCH_NAME == 'main' ? 'H 0 * * *' : '') } options { timestamps() @@ -9,7 +9,7 @@ pipeline { } stages { stage('test') { - when { branch 'master' } + when { branch 'main' } steps { dir('app/src/androidTest/java/org/mozilla/fenix/syncIntegration') { sh 'pipenv install' @@ -22,7 +22,7 @@ pipeline { post { always { script { - if (env.BRANCH_NAME == 'master') { + if (env.BRANCH_NAME == 'main') { publishHTML(target: [ allowMissing: false, alwaysLinkToLastBuild: true, @@ -36,7 +36,7 @@ pipeline { failure { script { - if (env.BRANCH_NAME == 'master') { + if (env.BRANCH_NAME == 'main') { slackSend( color: 'danger', message: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL}HTML_20Report/)") diff --git a/app/benchmark.gradle b/app/benchmark.gradle new file mode 100644 index 000000000..9238a821e --- /dev/null +++ b/app/benchmark.gradle @@ -0,0 +1,66 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +// This comment contains the central documentation for how we configured Jetpack Benchmark. Currently: +// - microbenchmark: configured differently than recommended (see inline notes below) +// - macrobenchmark: not configured +// +// To run our benchmarks, you need to set the "benchmark" gradle property. You can: +// - (preferred) Run via the command line (change the class you run on): +// ./gradlew -Pbenchmark app:connectedCheck -Pandroid.testInstrumentationRunnerArguments.class=org.mozilla.fenix.perf.SampleBenchmark +// - Use the IDE. Temporarily set the "benchmark" property in app/build.gradle with "ext.benchmark=true" +// near the top of the file. DO NOT COMMIT THIS. +// - (note: I was unable to get IDE run configurations working) +// +// To get the results, look at this file (we recommend using the median; results are in nanoseconds): +// app/build/outputs/connected_android_test_additional_output/nightlyAndroidTest/connected//org.mozilla.fenix-benchmarkData.json +// +// I was unable to get the results to print directly in Android Studio (perhaps it's my device). +// +// The official documentation suggests configuring microbenchmark in a separate module. This would +// require any benchmarked code to be in a library module, not the :app module (see below). To avoid +// this requirement, we created the "benchmark" gradle property. +// +// For the most accurate results, the documentation recommends running tests on rooted devices with +// the CPU clock locked. +// +// See https://developer.android.com/studio/profile/benchmark#what-to-benchmark for when writing a +// jetpack microbenchmark is a good fit. + +// I think `android` represents this object: +// https://google.github.io/android-gradle-dsl/3.3/com.android.build.gradle.AppExtension.html +ext.maybeConfigForJetpackBenchmark = { android -> + if (!project.hasProperty("benchmark")) { + return + } + + // The official documentation https://developer.android.com/studio/profile/benchmark#full-setup + // recommends setting up the Microbenchmark library in a separate module from your app: AFAICT, + // the reason for this is to prevent the benchmarks from being configured against debug + // builds. We chose not to do this because it's a lot of work to pull code out into a + // separate module just to benchmark it. We were able to replicate the outcome by setting + // this testBuildType property. + android.testBuildType "nightly" + + // WARNING: our proguard configuration for androidTest is not set up correctly so the tests + // fail if we don't disable minification. DISABLING MINIFICATION PRODUCES BENCHMARKS THAT ARE + // LESS REPRESENTATIVE TO THE USER EXPERIENCE, however, so we made this tradeoff to reduce + // implementation time. + project.ext.disableOptimization = true + + android.defaultConfig { + // WARNING: the benchmark framework warns you if you're running the test in a configuration + // that will compromise the accuracy of the results. Unfortunately, I couldn't get everything + // working so I had to suppress some things. + // + // - ACTIVITY-MISSING: we're supposed to use the test instrumentation runner, + // "androidx.benchmark.junit4.AndroidBenchmarkRunner". However, when I do so, I get an error + // that we're unable to launch the activity. My understanding is that this runner will use an + // "IsolationActivity" to reduce the impact of other work on the device from affecting the benchmark + // and to opt into a lower-max CPU frequency on unrooted devices that support it + // - UNLOCKED: ./gradlew lockClocks, which locks the CPU frequency, fails on my device. See + // https://issuetracker.google.com/issues/176836267 for potential workarounds. + testInstrumentationRunnerArgument 'androidx.benchmark.suppressErrors', 'ACTIVITY-MISSING,UNLOCKED' + } +} diff --git a/app/build.gradle b/app/build.gradle index 5027c1bb3..fc09c6bb7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,6 +8,7 @@ apply plugin: 'kotlin-android-extensions' apply plugin: 'jacoco' apply plugin: 'androidx.navigation.safeargs.kotlin' apply plugin: 'com.google.android.gms.oss-licenses-plugin' +apply plugin: 'androidx.benchmark' import com.android.build.OutputFile @@ -17,9 +18,12 @@ import org.gradle.internal.logging.text.StyledTextOutputFactory import static org.gradle.api.tasks.testing.TestResult.ResultType +apply from: 'benchmark.gradle' + android { compileSdkVersion Config.compileSdkVersion + project.maybeConfigForJetpackBenchmark(it) if (project.hasProperty("testBuildType")) { // Allowing to configure the test build type via command line flag (./gradlew -PtestBuildType=beta ..) // in order to run UI tests against other build variants than debug in automation. @@ -75,7 +79,7 @@ android { // in automation for UI testing non-debug builds. shrinkResources !project.hasProperty("disableOptimization") minifyEnabled !project.hasProperty("disableOptimization") - proguardFiles 'proguard-android-optimize-3.5.0-modified.txt', 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' matchingFallbacks = ['release'] // Use on the "release" build type in dependencies (AARs) if (gradle.hasProperty("localProperties.autosignReleaseWithDebugKey")) { @@ -174,6 +178,10 @@ android { } + buildFeatures { + viewBinding true + } + aaptOptions { // All JavaScript code used internally by GeckoView is packaged in a // file called omni.ja. If this file is compressed in the APK, @@ -200,7 +208,6 @@ android { } beta { java.srcDirs = ['src/migration/java'] - manifest.srcFile "src/migration/AndroidManifest.xml" } release { java.srcDirs = ['src/migration/java'] @@ -253,6 +260,14 @@ android { minHeapSize = "1024m" } } + + buildFeatures { + compose true + } + + composeOptions { + kotlinCompilerExtensionVersion = Versions.androidx_compose + } } android.applicationVariants.all { variant -> @@ -293,12 +308,15 @@ android.applicationVariants.all { variant -> println("versionName override: $versionName") variant.outputs.each { output -> + def isMozillaOnline = project.hasProperty("mozillaOnline") || gradle.hasProperty("localProperties.mozillaOnline") def abi = output.getFilter(OutputFile.ABI) + // If it is a Mozilla Online build, use a unified version code of armeabi-v7a + def arch = (isMozillaOnline) ? "armeabi-v7a" : abi // We use the same version code generator, that we inherited from Fennec, across all channels - even on // channels that never shipped a Fennec build. - def versionCodeOverride = Config.generateFennecVersionCode(abi) + def versionCodeOverride = Config.generateFennecVersionCode(arch) - println("versionCode for $abi = $versionCodeOverride") + println("versionCode for $abi = $versionCodeOverride, isMozillaOnline = $isMozillaOnline") output.versionNameOverride = versionName output.versionCodeOverride = versionCodeOverride @@ -428,10 +446,6 @@ configurations { // correct runtime classpath when invoked with Android Studio's built-in JUnit test runner. // Success! jnaForTest - // Robolectric, through `com.google.android.apps.common.testing.accessibility.framework` - // depends on an old version of protobuf that conflict with the Application Services one. - // See: https://github.com/mozilla/application-services/issues/2952 - all*.exclude group: 'com.google.protobuf', module: 'protobuf-java' } dependencies { @@ -465,7 +479,6 @@ dependencies { implementation Deps.mozilla_browser_icons implementation Deps.mozilla_browser_menu implementation Deps.mozilla_browser_menu2 - implementation Deps.mozilla_browser_session implementation Deps.mozilla_browser_session_storage implementation Deps.mozilla_browser_state implementation Deps.mozilla_browser_storage_sync @@ -538,6 +551,10 @@ dependencies { debugImplementation Deps.leakcanary forkDebugImplementation Deps.leakcanary + implementation Deps.androidx_compose_ui + implementation Deps.androidx_compose_ui_tooling + implementation Deps.androidx_compose_foundation + implementation Deps.androidx_compose_material implementation Deps.androidx_legacy implementation Deps.androidx_biometric implementation Deps.androidx_paging @@ -588,6 +605,7 @@ dependencies { androidTestImplementation Deps.androidx_junit androidTestImplementation Deps.androidx_work_testing + androidTestImplementation Deps.androidx_benchmark_junit4 androidTestImplementation Deps.mockwebserver testImplementation Deps.mozilla_support_test testImplementation Deps.mozilla_support_test_libstate @@ -612,6 +630,11 @@ dependencies { if (project.hasProperty("coverage")) { tasks.withType(Test).configureEach { jacoco.includeNoLocationClasses = true + jacoco.excludes = ['jdk.internal.*'] + } + + jacoco { + toolVersion = "0.8.7" } android.applicationVariants.all { variant -> @@ -779,14 +802,3 @@ ext.updateExtensionVersion = { task, extDir -> expand(values) } } - -tasks.register("updateAdsExtensionVersion", Copy) { task -> - updateExtensionVersion(task, 'src/main/assets/extensions/ads') -} - -tasks.register("updateCookiesExtensionVersion", Copy) { task -> - updateExtensionVersion(task, 'src/main/assets/extensions/cookies') -} - -preBuild.dependsOn "updateAdsExtensionVersion" -preBuild.dependsOn "updateCookiesExtensionVersion" diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml index 5e2116b6a..7c62294db 100644 --- a/app/lint-baseline.xml +++ b/app/lint-baseline.xml @@ -1,117 +1,63303 @@ - - + + id="ObsoleteLintCustomCheck" + message="Lint found an issue registry (`com.google.firebase.installations.lint.Checks`) which is older than the current API level; these checks may not work correctly. Recompile the checks against the latest version. Custom check API version is 2 (3.2), current lint API level is 8 (4.1)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + line="144" + column="9"/> + id="UnusedAttribute" + message="Attribute `lineHeight` is only used in API level 28 and higher (current min is 21)" + errorLine1=" android:lineHeight="18sp"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/res/layout/custom_search_engine.xml" + line="59" + column="9"/> + id="UnusedAttribute" + message="Attribute `android:foreground` has no effect on API levels lower than 23 (current min is 21)" + errorLine1=" android:foreground="?android:attr/selectableItemBackground">" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/res/layout/list_element.xml" + line="16" + column="5"/> + id="UnusedAttribute" + message="Attribute `font` is only used in API level 26 and higher (current min is 21)" + errorLine1=" android:font="@font/metropolis_black"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/res/font/metropolis.xml" + line="7" + column="9"/> + id="UnusedAttribute" + message="Attribute `fontStyle` is only used in API level 26 and higher (current min is 21)" + errorLine1=" android:fontStyle="normal"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/res/font/metropolis.xml" + line="8" + column="9"/> + id="UnusedAttribute" + message="Attribute `fontWeight` is only used in API level 26 and higher (current min is 21)" + errorLine1=" android:fontWeight="900"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/res/font/metropolis.xml" + line="9" + column="9"/> + id="UnusedAttribute" + message="Attribute `font` is only used in API level 26 and higher (current min is 21)" + errorLine1=" android:font="@font/metropolis_extrabold"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/res/font/metropolis.xml" + line="16" + column="9"/> + id="UnusedAttribute" + message="Attribute `fontStyle` is only used in API level 26 and higher (current min is 21)" + errorLine1=" android:fontStyle="normal"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + column="9"/> + id="UnusedAttribute" + message="Attribute `fontStyle` is only used in API level 26 and higher (current min is 21)" + errorLine1=" android:fontStyle="normal"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/res/font/metropolis.xml" + line="44" + column="9"/> + id="UnusedAttribute" + message="Attribute `fontWeight` is only used in API level 26 and higher (current min is 21)" + errorLine1=" android:fontWeight="500"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~"> + file="src/main/res/font/metropolis.xml" + line="45" + column="9"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/lint.xml b/app/lint.xml index 7f7c59681..33a1a4d1b 100644 --- a/app/lint.xml +++ b/app/lint.xml @@ -50,7 +50,7 @@ - + diff --git a/app/metrics.yaml b/app/metrics.yaml index c68e04cce..f6631e170 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -8,110 +8,6 @@ no_lint: - CATEGORY_GENERIC events: - app_opened_all_startup: - type: event - description: | - **This probe has a known flaw:** for COLD start up, it doesn't take into - account if the process is already running when the app starts, possibly - inflating results (e.g. a Service started the process 20min ago and only - now is HomeActivity launching). See the `cold_*_app_to_first_frame` probes - for a replacement. -

- 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]. - 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 - Some applications such as gmail launches the default browser in the - background. So when we eventually click a link, browser is already - started in the background. This means that custom_tab will mostly - report `warm` startup type. - source: - description: | - 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. - 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]. - first_frame_pre_draw_nanos: - description: | - the number of nanoseconds the application took to launch. This is the - time difference between application launch(user pressing app_icon, - launching a link) and until the first view is about to be drawn - on the screen. If the time is not captured, this extra key will - not be reported. - 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 - - https://github.com/mozilla-mobile/fenix/issues/10069 - - https://github.com/mozilla-mobile/fenix/issues/19923 - 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 - - https://github.com/mozilla-mobile/fenix/pull/15605#issuecomment-702365594 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - esmyth@mozilla.com - - perf-android-fe@mozilla.com - expires: "2021-12-01" - app_received_intent: - type: event - description: | - 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. - extra_keys: - source: - description: | - The method used to open Fenix. Possible values are `app_icon`, - `custom_tab`, `link` or `unknown` - bugs: - - https://github.com/mozilla-mobile/fenix/issues/11830 - - https://github.com/mozilla-mobile/fenix/issues/19923 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/11940/ - - https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - notification_emails: - - esmyth@mozilla.com - - perf-android-fe@mozilla.com - expires: "2021-12-01" app_opened: type: event description: | @@ -246,50 +142,42 @@ events: bugs: - https://github.com/mozilla-mobile/fenix/issues/18857 data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/18895 + - https://github.com/mozilla-mobile/fenix/pull/18982#pullrequestreview-635098629 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-10-01" - toolbar_menu_visible: + expires: "2022-04-01" + default_browser_notif_tapped: type: event description: | - The browser menu was displayed from toolbar menu + User tapped on the default browser notification bugs: - - https://github.com/mozilla-mobile/fenix/issues/18855 + - https://github.com/mozilla-mobile/fenix/issues/19847 data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/18895 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20311 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-10-01" - total_uri_count: - type: counter + expires: "2022-08-01" + toolbar_menu_visible: + type: event description: | - 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. - send_in_pings: - - metrics + The browser menu was displayed from toolbar menu bugs: - - https://github.com/mozilla-mobile/fenix/issues/1301 - - https://github.com/mozilla-mobile/fenix/issues/4456 + - https://github.com/mozilla-mobile/fenix/issues/18855 data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/1785 - - https://github.com/mozilla-mobile/fenix/pull/8314 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 + - https://github.com/mozilla-mobile/fenix/pull/18982#pullrequestreview-635098629 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-04-01" normal_and_private_uri_count: type: counter description: | @@ -459,11 +347,47 @@ events: - https://github.com/mozilla-mobile/fenix/issues/19923 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/16915 + - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com expires: "2021-12-10" + browser_toolbar_home_tapped: + type: event + description: | + An event that indicates that a user has tapped + home button on browser toolbar. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/19915 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/19936 + - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2021-12-10" + tab_view_changed: + type: event + description: | + Indicates that the user has changed their tab view + settings, either from the default or by personal + preference. + extra_keys: + type: + description: | + A string containing the name of the tab view the user tapped. + These items include: list or grid. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/19956 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/19959#issuecomment-882539619 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-05-10" onboarding: fxa_auto_signin: @@ -476,12 +400,13 @@ onboarding: - https://github.com/mozilla-mobile/fenix/pull/11867 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" fxa_manual_signin: type: event description: @@ -492,12 +417,13 @@ onboarding: - https://github.com/mozilla-mobile/fenix/pull/11867 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" privacy_notice: type: event description: @@ -508,12 +434,13 @@ onboarding: - https://github.com/mozilla-mobile/fenix/pull/11867 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" pref_toggled_private_browsing: type: event description: @@ -524,12 +451,13 @@ onboarding: - https://github.com/mozilla-mobile/fenix/pull/11867 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" pref_toggled_toolbar_position: type: event description: @@ -545,12 +473,13 @@ onboarding: - https://github.com/mozilla-mobile/fenix/pull/11867 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" pref_toggled_tracking_prot: type: event description: @@ -566,12 +495,13 @@ onboarding: - https://github.com/mozilla-mobile/fenix/pull/11867 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" pref_toggled_theme_picker: type: event description: @@ -587,12 +517,13 @@ onboarding: - https://github.com/mozilla-mobile/fenix/pull/11867 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" finish: type: event description: @@ -603,12 +534,13 @@ onboarding: - https://github.com/mozilla-mobile/fenix/pull/11867 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" search_shortcuts: selected: @@ -625,9 +557,10 @@ search_shortcuts: - https://github.com/mozilla-mobile/fenix/pull/1202#issuecomment-476870449 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" experiments_default_browser: toolbar_menu_clicked: @@ -637,13 +570,14 @@ experiments_default_browser: bugs: - https://github.com/mozilla-mobile/fenix/issues/18851 data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/18895 + - https://github.com/mozilla-mobile/fenix/pull/18982#pullrequestreview-635098629 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-10-01" + expires: "2022-04-01" toolbar_settings: changed_position: type: event @@ -659,11 +593,12 @@ toolbar_settings: - https://github.com/mozilla-mobile/fenix/pull/6608 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" crash_reporter: opened: @@ -746,11 +681,12 @@ login_dialog: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13050 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" cancelled: type: event description: | @@ -760,11 +696,12 @@ login_dialog: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13050 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" saved: type: event description: | @@ -774,11 +711,12 @@ login_dialog: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13050 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" never_save: type: event description: | @@ -788,58 +726,12 @@ login_dialog: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13050 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" - -find_in_page: - opened: - type: event - description: | - A user opened the find in page UI - bugs: - - https://github.com/mozilla-mobile/fenix/issues/1036 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - closed: - type: event - description: | - A user closed the find in page UI - bugs: - - https://github.com/mozilla-mobile/fenix/issues/1036 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - searched_page: - type: event - description: | - A user searched the page - bugs: - - https://github.com/mozilla-mobile/fenix/issues/1036 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" metrics: default_browser: @@ -855,11 +747,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" distribution_id: type: string lifetime: application @@ -895,11 +788,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/16942 - https://github.com/mozilla-mobile/fenix/pull/16942#issuecomment-742794701 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" has_mobile_bookmarks: type: boolean lifetime: application @@ -913,11 +807,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/16942 - https://github.com/mozilla-mobile/fenix/pull/16942#issuecomment-742794701 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" desktop_bookmarks_count: type: counter lifetime: application @@ -935,11 +830,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/16942 - https://github.com/mozilla-mobile/fenix/pull/16942#issuecomment-742794701 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" has_desktop_bookmarks: type: boolean lifetime: application @@ -953,11 +849,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/16942 - https://github.com/mozilla-mobile/fenix/pull/16942#issuecomment-742794701 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" top_sites_count: type: counter lifetime: application @@ -975,11 +872,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/9556 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" has_top_sites: type: boolean lifetime: application @@ -993,11 +891,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/9556 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" recently_used_pwa_count: type: counter lifetime: application @@ -1019,11 +918,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/11982#pullrequestreview-437963817 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" has_recent_pwas: type: boolean lifetime: application @@ -1038,11 +938,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/11982#pullrequestreview-437963817 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" search_count: type: labeled_counter description: | @@ -1067,66 +968,13 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/7310 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" - credit_cards_saved_count: - type: counter - lifetime: application - description: | - A counter that indicates the number of credit cards that have been - saved manually by the user. - send_in_pings: - - metrics - bugs: - - https://github.com/mozilla-mobile/fenix/issues/18711 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/19548#issuecomment-848811030 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-10-01" - credit_cards_deleted_count: - type: counter - lifetime: application - description: | - A counter that indicates the number of credit cards that have been - deleted by the user. - send_in_pings: - - metrics - bugs: - - https://github.com/mozilla-mobile/fenix/issues/18711 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/19548#issuecomment-848811030 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-10-01" - credit_cards_autofill_count: - type: counter - lifetime: application - description: | - A counter that indicates the number of times the user has autofilled - a credit card. - send_in_pings: - - metrics - bugs: - - https://github.com/mozilla-mobile/fenix/issues/18711 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/19548#issuecomment-848811030 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-10-01" + expires: "2022-02-01" mozilla_products: type: string_list lifetime: application @@ -1144,12 +992,13 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/5216 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" default_moz_browser: type: string lifetime: application @@ -1165,12 +1014,13 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/5216 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" adjust_campaign: type: string lifetime: application @@ -1187,11 +1037,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/5579 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" adjust_ad_group: type: string lifetime: application @@ -1208,11 +1059,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/9253 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" adjust_creative: type: string lifetime: application @@ -1229,11 +1081,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/9253 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" adjust_network: type: string lifetime: application @@ -1250,11 +1103,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/9253 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" toolbar_position: type: string lifetime: application @@ -1268,11 +1122,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/6608 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" close_tab_setting: type: string lifetime: application @@ -1286,11 +1141,12 @@ metrics: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/15811#issuecomment-706402952 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" tab_view_setting: type: string lifetime: application @@ -1304,11 +1160,12 @@ metrics: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/15811#issuecomment-706402952 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" search_widget_installed: type: boolean lifetime: application @@ -1322,11 +1179,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/10958 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" tabs_open_count: type: counter lifetime: application @@ -1344,11 +1202,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/12024 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" has_open_tabs: type: boolean lifetime: application @@ -1362,11 +1221,12 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/12024 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" start_reason_process_error: type: boolean description: | @@ -1378,12 +1238,13 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/18426 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18632#issue-600193452 + - https://github.com/mozilla-mobile/fenix/pull/20623#issue-701630599 data_sensitivity: - technical notification_emails: - perf-android-fe@mozilla.com - mcomella@mozilla.com - expires: "2021-08-11" + expires: "2022-02-01" start_reason_activity_error: type: boolean description: | @@ -1395,12 +1256,13 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/18426 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18632#issue-600193452 + - https://github.com/mozilla-mobile/fenix/pull/20623#issue-701630599 data_sensitivity: - technical notification_emails: - perf-android-fe@mozilla.com - mcomella@mozilla.com - expires: "2021-08-11" + expires: "2022-02-01" preferences: search_suggestions_enabled: @@ -1416,11 +1278,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" remote_debugging_enabled: type: boolean description: > @@ -1434,11 +1297,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" telemetry_enabled: type: boolean description: > @@ -1454,11 +1318,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" enhanced_tracking_protection: type: string description: > @@ -1473,11 +1338,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" bookmarks_suggestion: type: boolean description: > @@ -1491,11 +1357,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" browsing_history_suggestion: type: boolean description: > @@ -1509,11 +1376,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" clipboard_suggestions_enabled: type: boolean description: > @@ -1527,11 +1395,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" search_shortcuts_enabled: type: boolean description: > @@ -1545,29 +1414,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" - open_links_in_private: - type: boolean - description: > - Whether or not the user has enabled open links in a private tab. - default: false - send_in_pings: - - metrics - bugs: - - https://github.com/mozilla-mobile/fenix/issues/11118 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/11211 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" signed_in_sync: type: boolean description: > @@ -1581,11 +1433,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" sync_items: type: string_list description: > @@ -1601,11 +1454,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" voice_search_enabled: type: boolean description: > @@ -1619,30 +1473,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" - private_search_suggestions: - type: boolean - description: > - Whether or not the user has enabled showing search suggestions - in private mode. - default: false (we prompt the user, asking them to make a selection) - send_in_pings: - - metrics - bugs: - - https://github.com/mozilla-mobile/fenix/issues/11118 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/11211 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" toolbar_position_setting: type: string description: > @@ -1656,11 +1492,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" accessibility_services: type: string_list description: > @@ -1675,11 +1512,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11211 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" open_links_in_app_enabled: type: boolean description: > @@ -1693,11 +1531,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11446 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" user_theme: type: string description: > @@ -1711,11 +1550,12 @@ preferences: - https://github.com/mozilla-mobile/fenix/pull/11446 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" search.default_engine: code: @@ -1735,12 +1575,13 @@ search.default_engine: - https://github.com/mozilla-mobile/fenix/pull/5216 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" name: type: string lifetime: application @@ -1758,12 +1599,13 @@ search.default_engine: - https://github.com/mozilla-mobile/fenix/pull/5216 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" submission_url: type: string lifetime: application @@ -1782,12 +1624,13 @@ search.default_engine: - https://github.com/mozilla-mobile/fenix/pull/5216 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" bookmarks_management: open: @@ -2140,11 +1983,12 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" closed: type: event description: | @@ -2155,11 +1999,12 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" use_email: type: event description: | @@ -2171,11 +2016,12 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/pull/9835#pullrequestreview-398641844 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" use_email_problem: type: event description: | @@ -2186,11 +2032,12 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/pull/9835#pullrequestreview-398641844 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" sign_in: type: event description: | @@ -2202,11 +2049,12 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" sign_out: type: event description: | @@ -2218,11 +2066,12 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" sign_up: type: event description: | @@ -2233,12 +2082,13 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" paired: type: event description: | @@ -2250,12 +2100,13 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" recovered: type: event description: | @@ -2267,12 +2118,13 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" other_external: type: event description: | @@ -2284,12 +2136,13 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" scan_pairing: type: event description: | @@ -2300,11 +2153,12 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" sync_account: opened: @@ -2317,11 +2171,12 @@ sync_account: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" sync_now: type: event description: | @@ -2332,11 +2187,12 @@ sync_account: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" send_tab: type: event description: | @@ -2347,11 +2203,12 @@ sync_account: - https://github.com/mozilla-mobile/fenix/pull/5106 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" sign_in_to_send_tab: type: event description: | @@ -2362,11 +2219,12 @@ sync_account: - https://github.com/mozilla-mobile/fenix/pull/5106 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" history: opened: @@ -2379,11 +2237,12 @@ history: - https://github.com/mozilla-mobile/fenix/pull/3940 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" removed: type: event description: | @@ -2394,11 +2253,12 @@ history: - https://github.com/mozilla-mobile/fenix/pull/3940 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" removed_all: type: event description: | @@ -2409,11 +2269,12 @@ history: - https://github.com/mozilla-mobile/fenix/pull/3940 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" shared: type: event description: | @@ -2424,11 +2285,12 @@ history: - https://github.com/mozilla-mobile/fenix/pull/3940 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" opened_item: type: event description: | @@ -2501,78 +2363,23 @@ history: - android-probes@mozilla.com expires: "2022-08-01" -tip: - displayed: +reader_mode: + available: type: event description: | - The tip was displayed - extra_keys: - identifier: - description: "The identifier of the tip displayed" - bugs: - - https://github.com/mozilla-mobile/fenix/issues/9328 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/9836 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - pressed: - type: event - description: | - The tip's button was pressed - extra_keys: - identifier: - description: "The identifier of the tip the action was taken on" - bugs: - - https://github.com/mozilla-mobile/fenix/issues/9328 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/9836 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - closed: - type: event - description: | - The tip was closed - extra_keys: - identifier: - description: "The identifier of the tip closed" - bugs: - - https://github.com/mozilla-mobile/fenix/issues/9328 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/9836 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - -reader_mode: - available: - type: event - description: | - Reader mode is available for the current page + Reader mode is available for the current page bugs: - https://github.com/mozilla-mobile/fenix/issues/2267 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3941 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" opened: type: event description: | @@ -2583,11 +2390,12 @@ reader_mode: - https://github.com/mozilla-mobile/fenix/pull/3941 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" closed: type: event description: | @@ -2598,11 +2406,12 @@ reader_mode: - https://github.com/mozilla-mobile/fenix/pull/4328 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" appearance: type: event description: | @@ -2613,43 +2422,12 @@ reader_mode: - https://github.com/mozilla-mobile/fenix/pull/3941 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" - -tabs_tray.cfr: - dismiss: - type: event - description: | - A user dismisses the tabs tray CFR. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/16485 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/17442 - - https://github.com/mozilla-mobile/fenix/issues/16485#issuecomment-759641324 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - go_to_settings: - type: event - description: | - A user selects the CFR option to navigate to settings. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/16485 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/17442 - - https://github.com/mozilla-mobile/fenix/issues/16485#issuecomment-759641324 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" tabs_tray: opened: @@ -2662,11 +2440,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" closed: type: event description: | @@ -2677,11 +2456,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" opened_existing_tab: type: event description: | @@ -2692,11 +2472,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" closed_existing_tab: type: event description: | @@ -2707,11 +2488,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" private_mode_tapped: type: event description: | @@ -2722,11 +2504,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" normal_mode_tapped: type: event description: | @@ -2737,11 +2520,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" synced_mode_tapped: type: event description: | @@ -2766,11 +2550,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" new_private_tab_tapped: type: event description: | @@ -2781,11 +2566,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" menu_opened: type: event description: | @@ -2796,11 +2582,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" save_to_collection: type: event description: | @@ -2811,11 +2598,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" share_all_tabs: type: event description: | @@ -2827,11 +2615,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" close_all_tabs: type: event description: | @@ -2843,11 +2632,12 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/pull/12036 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" collections: renamed: @@ -3111,11 +2901,12 @@ search_widget: - https://github.com/mozilla-mobile/fenix/pull/4714 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" voice_button: type: event description: | @@ -3126,44 +2917,12 @@ search_widget: - https://github.com/mozilla-mobile/fenix/pull/4714 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" - -private_browsing_mode: - snackbar_undo: - type: event - description: | - A user pressed the "undo" button in the snackbar that is shown when the - garbage icon is tapped. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/4658 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/4968 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - notification_tapped: - type: event - description: | - A user pressed the private browsing mode notification itself. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/4658 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/4968 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" contextual_hint.tracking_protection: display: @@ -3248,11 +3007,12 @@ tracking_protection: - https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" panel_settings: type: event description: | @@ -3263,11 +3023,12 @@ tracking_protection: - https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" etp_shield: type: event description: | @@ -3278,11 +3039,12 @@ tracking_protection: - https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" etp_tracker_list: type: event description: | @@ -3294,11 +3056,12 @@ tracking_protection: - https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" etp_settings: type: event description: | @@ -3309,11 +3072,12 @@ tracking_protection: - https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" etp_setting_changed: type: event description: | @@ -3330,108 +3094,12 @@ tracking_protection: - https://github.com/mozilla-mobile/fenix/pull/11383 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" - -private_browsing_shortcut: - create_shortcut: - type: event - description: | - A user pressed the "Add private browsing shortcut" button in settings. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/4658 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/5194 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - cfr_add_shortcut: - type: event - description: | - A user pressed the "Add shortcut" button when the contextual feature - recommender appeared. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/4658 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/5194 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - cfr_cancel: - type: event - description: | - A user pressed the "No thanks" button when the contextual feature - recommender appeared. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/4658 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/5194 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - pinned_shortcut_priv: - type: event - description: | - A user pressed the pinned private shortcut in Android home screen, opening - up a new private search. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/4658 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/5194 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - static_shortcut_tab: - type: event - description: | - A user pressed the long-press shortcut "Open new tab", opening up a new - search. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/4658 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/5194 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - static_shortcut_priv: - type: event - description: | - A user pressed the long-press shortcut "Open new private tab", opening up - a new private search. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/4658 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/5194 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" tab: media_play: @@ -3444,11 +3112,12 @@ tab: - https://github.com/mozilla-mobile/fenix/pull/5266 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" no_lint: - COMMON_PREFIX media_pause: @@ -3461,11 +3130,12 @@ tab: - https://github.com/mozilla-mobile/fenix/pull/5266 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" media_notification: play: @@ -3478,11 +3148,12 @@ media_notification: - https://github.com/mozilla-mobile/fenix/pull/5520 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" pause: type: event description: | @@ -3493,11 +3164,12 @@ media_notification: - https://github.com/mozilla-mobile/fenix/pull/5520 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" media_state: play: @@ -3510,11 +3182,12 @@ media_state: - https://github.com/mozilla-mobile/fenix/pull/6463 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" pause: type: event description: | @@ -3525,11 +3198,12 @@ media_state: - https://github.com/mozilla-mobile/fenix/pull/6463 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" stop: type: event description: | @@ -3540,11 +3214,12 @@ media_state: - https://github.com/mozilla-mobile/fenix/pull/6463 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" fullscreen: type: event description: | @@ -3554,11 +3229,12 @@ media_state: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/16833 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" picture_in_picture: type: event description: | @@ -3568,11 +3244,12 @@ media_state: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/16833 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" logins: open_logins: @@ -3585,11 +3262,12 @@ logins: - https://github.com/mozilla-mobile/fenix/pull/6352 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" open_individual_login: type: event description: | @@ -3600,11 +3278,12 @@ logins: - https://github.com/mozilla-mobile/fenix/pull/6352 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" copy_login: type: event description: | @@ -3615,11 +3294,12 @@ logins: - https://github.com/mozilla-mobile/fenix/pull/6352 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" view_password_login: type: event description: | @@ -3630,11 +3310,12 @@ logins: - https://github.com/mozilla-mobile/fenix/pull/6352 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" save_logins_setting_changed: type: event description: | @@ -3650,11 +3331,12 @@ logins: - https://github.com/mozilla-mobile/fenix/pull/7767 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" open_login_editor: type: event description: | @@ -3665,11 +3347,12 @@ logins: - https://github.com/mozilla-mobile/fenix/issues/11208 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" delete_saved_login: type: event description: | @@ -3680,257 +3363,28 @@ logins: - https://github.com/mozilla-mobile/fenix/issues/11208 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" save_edited_login: type: event description: | - A user saves changes made to an individual login - bugs: - - https://github.com/mozilla-mobile/fenix/issues/10173 - data_reviews: - - https://github.com/mozilla-mobile/fenix/issues/11208 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - -download_notification: - resume: - type: event - description: | - A user resumed a download in the download notification - bugs: - - 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 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-07-01" - pause: - type: event - description: | - A user paused a download in the download notification - bugs: - - 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 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-07-01" - cancel: - type: event - description: | - A user cancelled a download in the download notification - bugs: - - 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 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-07-01" - try_again: - type: event - description: | - A user tapped on try again when a download fails in the download - notification - bugs: - - 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 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-07-01" - open: - type: event - description: | - A user opened a downloaded file in the download notification - bugs: - - 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 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-07-01" - in_app_open: - type: event - description: | - A user opened a downloaded file in the in-app notification link - bugs: - - 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 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-07-01" - in_app_try_again: - type: event - description: | - A user tapped on try again when a download fails in the in-app - notification link - bugs: - - 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 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-07-01" - -downloads_misc: - download_added: - type: event - description: - A counter for how many times something is downloaded in the app. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/11578 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/16730 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - notification_emails: - - android-probes@mozilla.com - expires: "2021-07-01" - -downloads_management: - downloads_screen_opened: - type: event - description: > - A counter for the number of times users access the "Downloads" folder - inside the app. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/15367 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/16728 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - notification_emails: - - android-probes@mozilla.com - expires: "2021-07-01" - - item_opened: - type: event - description: > - A counter for how often a user tap to opens a download from inside the - "Downloads" folder. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/15367 - - https://github.com/mozilla-mobile/fenix/issues/19923 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/16728 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - notification_emails: - - android-probes@mozilla.com - expires: "2021-12-01" - - item_deleted: - type: event - description: > - A counter for how often a user deletes one / more downloads at a time. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/15367 - - https://github.com/mozilla-mobile/fenix/issues/19923 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/16728 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - notification_emails: - - android-probes@mozilla.com - expires: "2021-12-01" - -user_specified_search_engines: - custom_engine_added: - type: event - description: | - A user added a new custom search engine - bugs: - - https://github.com/mozilla-mobile/fenix/issues/5884 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/6918 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - no_lint: - - COMMON_PREFIX - custom_engine_deleted: - type: event - description: | - A user deleted a custom search engine - bugs: - - https://github.com/mozilla-mobile/fenix/issues/5884 - - https://github.com/mozilla-mobile/fenix/issues/7881 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/6918 - - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - -search_suggestions: - enable_in_private: - type: event - description: | - A user enabled receiving search suggestions in private sessions + A user saves changes made to an individual login bugs: - - https://github.com/mozilla-mobile/fenix/issues/6070 + - https://github.com/mozilla-mobile/fenix/issues/10173 data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/6746 + - https://github.com/mozilla-mobile/fenix/issues/11208 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" voice_search: tapped: @@ -3943,11 +3397,12 @@ voice_search: - https://github.com/mozilla-mobile/fenix/pull/10785 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" top_sites: open_default: @@ -3960,11 +3415,12 @@ top_sites: - https://github.com/mozilla-mobile/fenix/pull/10752 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" open_google_search_attribution: type: event description: | @@ -3974,11 +3430,12 @@ top_sites: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/17637 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" open_frecency: type: event description: | @@ -3989,11 +3446,12 @@ top_sites: - https://github.com/mozilla-mobile/fenix/pull/15136 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" open_pinned: type: event description: | @@ -4004,11 +3462,12 @@ top_sites: - https://github.com/mozilla-mobile/fenix/pull/15136 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" swipe_carousel: type: event description: | @@ -4023,11 +3482,12 @@ top_sites: - https://github.com/mozilla-mobile/fenix/pull/15136 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" long_press: type: event description: | @@ -4042,11 +3502,12 @@ top_sites: - https://github.com/mozilla-mobile/fenix/pull/15136 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" open_in_new_tab: type: event description: | @@ -4057,11 +3518,12 @@ top_sites: - https://github.com/mozilla-mobile/fenix/pull/7523 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" open_in_private_tab: type: event description: | @@ -4072,11 +3534,12 @@ top_sites: - https://github.com/mozilla-mobile/fenix/pull/7523 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" remove: type: event description: | @@ -4087,45 +3550,12 @@ top_sites: - https://github.com/mozilla-mobile/fenix/pull/7523 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" - -about_page: - support_tapped: - type: event - description: | - A user tapped on "Support" item from About page - bugs: - - 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 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-07-01" - privacy_notice_tapped: - type: event - description: | - A user tapped on "Privacy notice" item from About page - bugs: - - 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 - - https://github.com/mozilla-mobile/fenix/pull/18143 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-07-01" + expires: "2022-02-01" app_theme: dark_theme_selected: @@ -4162,14 +3592,14 @@ pocket: - https://github.com/mozilla-mobile/fenix/pull/8098 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" no_lint: - COMMON_PREFIX - pocket_top_site_removed: type: event description: | @@ -4180,11 +3610,12 @@ pocket: - https://github.com/mozilla-mobile/fenix/pull/8098 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" first_session: campaign: @@ -4199,12 +3630,13 @@ first_session: - https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" network: type: string send_in_pings: @@ -4217,12 +3649,13 @@ first_session: - https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" adgroup: type: string send_in_pings: @@ -4235,12 +3668,13 @@ first_session: - https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586480836 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" creative: send_in_pings: - first-session @@ -4253,12 +3687,13 @@ first_session: - https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" timestamp: send_in_pings: - first-session @@ -4273,12 +3708,13 @@ first_session: - https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - technical - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" browser.search: with_ads: @@ -4294,11 +3730,12 @@ browser.search: - https://github.com/mozilla-mobile/fenix/pull/10112 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20230#issuecomment-879244938 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-07-01" ad_clicks: type: labeled_counter description: | @@ -4312,11 +3749,12 @@ browser.search: - https://github.com/mozilla-mobile/fenix/pull/10112 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20230#issuecomment-879244938 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-07-01" in_content: type: labeled_counter description: | @@ -4329,11 +3767,12 @@ browser.search: - https://github.com/mozilla-mobile/fenix/pull/10167 - https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20230#issuecomment-879244938 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-07-01" addons: open_addons_in_settings: @@ -4471,6 +3910,7 @@ addons: startup.timeline: framework_primary: + disabled: true send_in_pings: - startup-timeline type: timespan @@ -4507,6 +3947,7 @@ startup.timeline: - mcomella@mozilla.com expires: "2021-08-01" framework_secondary: + disabled: true send_in_pings: - startup-timeline type: timespan @@ -4527,6 +3968,7 @@ startup.timeline: - mcomella@mozilla.com expires: "2021-08-01" framework_start_error: + disabled: true send_in_pings: - startup-timeline type: boolean @@ -4546,6 +3988,7 @@ startup.timeline: - mcomella@mozilla.com expires: "2021-08-01" framework_start_read_error: + disabled: true send_in_pings: - startup-timeline type: boolean @@ -4565,6 +4008,7 @@ startup.timeline: - mcomella@mozilla.com expires: "2021-08-01" clock_ticks_per_second_v2: + disabled: true send_in_pings: - startup-timeline type: quantity @@ -4619,12 +4063,13 @@ perf.startup: - https://github.com/mozilla-mobile/fenix/issues/18426 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18632#issue-600193452 + - https://github.com/mozilla-mobile/fenix/pull/20623#issue-701630599 data_sensitivity: - technical notification_emails: - perf-android-fe@mozilla.com - mcomella@mozilla.com - expires: "2021-08-11" + expires: "2022-02-01" cold_view_app_to_first_frame: type: timing_distribution time_unit: millisecond @@ -4646,12 +4091,13 @@ perf.startup: - https://github.com/mozilla-mobile/fenix/issues/18426 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18632#issue-600193452 + - https://github.com/mozilla-mobile/fenix/pull/20623#issue-701630599 data_sensitivity: - technical notification_emails: - perf-android-fe@mozilla.com - mcomella@mozilla.com - expires: "2021-08-11" + expires: "2022-02-01" cold_unknwn_app_to_first_frame: type: timing_distribution time_unit: millisecond @@ -4671,12 +4117,13 @@ perf.startup: - https://github.com/mozilla-mobile/fenix/issues/18426 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18632#issue-600193452 + - https://github.com/mozilla-mobile/fenix/pull/20623#issue-701630599 data_sensitivity: - technical notification_emails: - perf-android-fe@mozilla.com - mcomella@mozilla.com - expires: "2021-08-11" + expires: "2022-02-01" application_on_create: type: timing_distribution time_unit: millisecond @@ -4688,13 +4135,15 @@ perf.startup: - https://github.com/mozilla-mobile/fenix/issues/17969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/17973#issue-572183889 + - https://github.com/mozilla-mobile/fenix/pull/20623#issue-701630599 data_sensitivity: - technical notification_emails: - perf-android-fe@mozilla.com - mcomella@mozilla.com - expires: "2021-08-11" + expires: "2022-02-01" app_on_create_to_glean_init: + disabled: true type: timing_distribution time_unit: millisecond description: | @@ -4714,6 +4163,7 @@ perf.startup: - mcomella@mozilla.com expires: "2021-08-11" app_on_create_to_megazord_init: + disabled: true type: timing_distribution time_unit: millisecond description: | @@ -4731,6 +4181,7 @@ perf.startup: - mcomella@mozilla.com expires: "2021-08-11" app_on_create_to_setup_in_main: + disabled: true type: timing_distribution time_unit: millisecond description: | @@ -4750,6 +4201,7 @@ perf.startup: - mcomella@mozilla.com expires: "2021-08-11" home_activity_on_create: + disabled: true type: timing_distribution time_unit: millisecond description: | @@ -4765,6 +4217,7 @@ perf.startup: - mcomella@mozilla.com expires: "2021-08-11" home_activity_on_start: + disabled: true type: timing_distribution time_unit: millisecond description: | @@ -4785,6 +4238,7 @@ perf.startup: - mcomella@mozilla.com expires: "2021-08-11" home_fragment_on_create_view: + disabled: true type: timing_distribution time_unit: millisecond description: | @@ -4800,6 +4254,7 @@ perf.startup: - mcomella@mozilla.com expires: "2021-08-11" home_fragment_on_view_created: + disabled: true type: timing_distribution time_unit: millisecond description: | @@ -4815,6 +4270,7 @@ perf.startup: - mcomella@mozilla.com expires: "2021-08-11" base_bfragment_on_create_view: + disabled: true type: timing_distribution time_unit: millisecond description: | @@ -4830,6 +4286,7 @@ perf.startup: - mcomella@mozilla.com expires: "2021-08-11" base_bfragment_on_view_created: + disabled: true type: timing_distribution time_unit: millisecond description: | @@ -4913,12 +4370,13 @@ perf.startup: - https://github.com/mozilla-mobile/fenix/issues/18836 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/19028 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - perf-android-fe@mozilla.com - mcomella@mozilla.com - expires: "2021-10-09" + expires: "2022-04-01" perf.awesomebar: history_suggestions: @@ -5064,11 +4522,12 @@ autoplay: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13041#issuecomment-665777411 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" setting_changed: type: event description: | @@ -5084,11 +4543,12 @@ autoplay: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13041#issuecomment-665777411 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" storage.stats: query_stats_duration: @@ -5106,6 +4566,7 @@ storage.stats: - https://github.com/mozilla-mobile/fenix/pull/12876#issuecomment-666770732 - https://github.com/mozilla-mobile/fenix/pull/17704#issue-564299127 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20623#issue-701630599 data_sensitivity: - technical - interaction @@ -5113,7 +4574,7 @@ storage.stats: - android-probes@mozilla.com - perf-android-fe@mozilla.com - mcomella@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" app_bytes: send_in_pings: - metrics @@ -5132,6 +4593,7 @@ storage.stats: - https://github.com/mozilla-mobile/fenix/pull/12876#issuecomment-666770732 - https://github.com/mozilla-mobile/fenix/pull/17704#issue-564299127 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20623#issue-701630599 data_sensitivity: - technical - interaction @@ -5139,7 +4601,7 @@ storage.stats: - android-probes@mozilla.com - perf-android-fe@mozilla.com - mcomella@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" cache_bytes: send_in_pings: - metrics @@ -5155,6 +4617,7 @@ storage.stats: - https://github.com/mozilla-mobile/fenix/pull/12876#issuecomment-666770732 - https://github.com/mozilla-mobile/fenix/pull/17704#issue-564299127 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20623#issue-701630599 data_sensitivity: - technical - interaction @@ -5162,7 +4625,7 @@ storage.stats: - android-probes@mozilla.com - perf-android-fe@mozilla.com - mcomella@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" data_dir_bytes: send_in_pings: - metrics @@ -5180,6 +4643,7 @@ storage.stats: - https://github.com/mozilla-mobile/fenix/pull/12876#issuecomment-666770732 - https://github.com/mozilla-mobile/fenix/pull/17704#issue-564299127 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20623#issue-701630599 data_sensitivity: - technical - interaction @@ -5187,7 +4651,7 @@ storage.stats: - android-probes@mozilla.com - perf-android-fe@mozilla.com - mcomella@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" progressive_web_app: homescreen_tap: @@ -5200,12 +4664,13 @@ progressive_web_app: - https://github.com/mozilla-mobile/fenix/pull/11859 - https://github.com/mozilla-mobile/fenix/pull/18071 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-09-01" + expires: "2022-03-01" install_tap: type: event description: | @@ -5216,12 +4681,13 @@ progressive_web_app: - https://github.com/mozilla-mobile/fenix/pull/11859 - https://github.com/mozilla-mobile/fenix/pull/18071 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-09-01" + expires: "2022-03-01" foreground: type: event description: | @@ -5236,12 +4702,13 @@ progressive_web_app: - https://github.com/mozilla-mobile/fenix/pull/11859 - https://github.com/mozilla-mobile/fenix/pull/18071 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-09-01" + expires: "2022-03-01" background: type: event description: | @@ -5256,44 +4723,13 @@ progressive_web_app: - https://github.com/mozilla-mobile/fenix/pull/11859 - https://github.com/mozilla-mobile/fenix/pull/18071 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - erichards@mozilla.com - expires: "2021-09-01" - -master_password: - displayed: - type: event - description: | - The master password migration dialog was displayed - bugs: - - https://github.com/mozilla-mobile/fenix/pull/14468#issuecomment-684114534 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/14468#issuecomment-684114534 - - https://github.com/mozilla-mobile/fenix/pull/18071 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-09-01" - migration: - type: event - description: | - Logins were successfully migrated using a master password. - bugs: - - https://github.com/mozilla-mobile/fenix/pull/14468#issuecomment-684114534 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/14468#issuecomment-684114534 - - https://github.com/mozilla-mobile/fenix/pull/18071 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-09-01" + expires: "2022-03-01" tabs: setting_opened: @@ -5305,55 +4741,12 @@ tabs: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/15811#issuecomment-706402952 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" - -banner_open_in_app: - displayed: - type: event - description: | - Open in App banner was shown. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/16828 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/17049 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - dismissed: - type: event - description: | - User tapped 'dismiss' on Open in App banner. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/16828 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/17049 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" - go_to_settings: - type: event - description: | - User tapped 'go to settings' on Open in App banner. - bugs: - - https://github.com/mozilla-mobile/fenix/issues/16828 - data_reviews: - - https://github.com/mozilla-mobile/fenix/pull/17049 - - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 - data_sensitivity: - - interaction - notification_emails: - - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" contextual_menu: copy_tapped: @@ -5523,11 +4916,12 @@ synced_tabs: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18172 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" awesomebar: bookmark_suggestion_clicked: @@ -5539,11 +4933,12 @@ awesomebar: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18090 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" clipboard_suggestion_clicked: type: event description: | @@ -5553,11 +4948,12 @@ awesomebar: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18090 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" history_suggestion_clicked: type: event description: | @@ -5567,11 +4963,12 @@ awesomebar: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18090 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" search_action_clicked: type: event description: | @@ -5581,11 +4978,12 @@ awesomebar: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18090 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" search_suggestion_clicked: type: event description: | @@ -5595,11 +4993,12 @@ awesomebar: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18090 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" opened_tab_suggestion_clicked: type: event description: | @@ -5609,11 +5008,161 @@ awesomebar: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18090 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/20517#pullrequestreview-718069041 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-08-01" + expires: "2022-02-01" + +android_autofill: + supported: + type: boolean + description: | + Whether or not Android Autofill is supported by the device and is + supported for this user. + bugs: + - https://github.com/mozilla-mobile/android-components/issues/10301 + data_reviews: + - TBD + data_sensitivity: + - technical + notification_emails: + - android-probes@mozilla.com + - skaspari@mozilla.com + expires: "2022-01-31" + enabled: + type: boolean + description: | + Whether or not Firefox is the Android Autofill provider for this user. + provider. + bugs: + - https://github.com/mozilla-mobile/android-components/issues/10301 + data_reviews: + - TBD + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + - skaspari@mozilla.com + expires: "2022-01-31" + request_matching_logins: + type: event + description: | + The app received an Android Autofill request from the system and was + able to find matching logins for the request + bugs: + - https://github.com/mozilla-mobile/android-components/issues/10301 + data_reviews: + - TBD + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + - skaspari@mozilla.com + expires: "2022-01-31" + request_no_matching_logins: + type: event + description: | + The app received an Android Autofill request from the system and was + not able to find matching logins for the request + bugs: + - https://github.com/mozilla-mobile/android-components/issues/10301 + data_reviews: + - TBD + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + - skaspari@mozilla.com + expires: "2022-01-31" + search_displayed: + type: event + description: | + The user has selected the search option to manually search a + matching login + bugs: + - https://github.com/mozilla-mobile/android-components/issues/10301 + data_reviews: + - TBD + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + - skaspari@mozilla.com + expires: "2022-01-31" + search_item_selected: + type: event + description: | + The user has selected a search result for autofilling. + bugs: + - https://github.com/mozilla-mobile/android-components/issues/10301 + data_reviews: + - TBD + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + - skaspari@mozilla.com + expires: "2022-01-31" + unlock_cancelled: + type: event + description: | + The user needed to unlock the app in order to autofill + logins, but the process was cancelled. + bugs: + - https://github.com/mozilla-mobile/android-components/issues/10301 + data_reviews: + - TBD + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + - skaspari@mozilla.com + expires: "2022-01-31" + unlock_successful: + type: event + description: | + The user successfully unlock the app in order to autofill logins + bugs: + - https://github.com/mozilla-mobile/android-components/issues/10301 + data_reviews: + - TBD + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + - skaspari@mozilla.com + expires: "2022-01-31" + confirm_cancelled: + type: event + description: | + The user needed to confirm autofilling an unauthenticated + application and decided to cancel + bugs: + - https://github.com/mozilla-mobile/android-components/issues/10301 + data_reviews: + - TBD + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + - skaspari@mozilla.com + expires: "2022-01-31" + confirm_successful: + type: event + description: | + The user confirmed autofilling an unauthenticated application + bugs: + - https://github.com/mozilla-mobile/android-components/issues/10301 + data_reviews: + - TBD + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + - skaspari@mozilla.com + expires: "2022-01-31" home_menu: settings_item_clicked: @@ -5624,11 +5173,12 @@ home_menu: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18987 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-10-01" + expires: "2022-04-01" home_screen: home_screen_displayed: @@ -5639,11 +5189,12 @@ home_screen: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/19025 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-10-01" + expires: "2022-04-01" android_keystore_experiment: experiment_failure: @@ -5660,11 +5211,12 @@ android_keystore_experiment: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18333#pullrequestreview-612447395 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - technical notification_emails: - android-probes@mozilla.com - expires: "2021-09-01" + expires: "2022-03-01" get_failure: type: event description: | @@ -5679,11 +5231,12 @@ android_keystore_experiment: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18333#pullrequestreview-612447395 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - technical notification_emails: - android-probes@mozilla.com - expires: "2021-09-01" + expires: "2022-03-01" get_result: type: event description: | @@ -5698,11 +5251,12 @@ android_keystore_experiment: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18333#pullrequestreview-612447395 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - technical notification_emails: - android-probes@mozilla.com - expires: "2021-09-01" + expires: "2022-03-01" write_failure: type: event description: | @@ -5717,11 +5271,12 @@ android_keystore_experiment: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18333#pullrequestreview-612447395 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - technical notification_emails: - android-probes@mozilla.com - expires: "2021-09-01" + expires: "2022-03-01" write_success: type: event description: | @@ -5731,11 +5286,12 @@ android_keystore_experiment: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18333#pullrequestreview-612447395 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - technical notification_emails: - android-probes@mozilla.com - expires: "2021-09-01" + expires: "2022-03-01" reset: type: event description: | @@ -5746,11 +5302,12 @@ android_keystore_experiment: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18333#pullrequestreview-612447395 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - technical notification_emails: - android-probes@mozilla.com - expires: "2021-09-01" + expires: "2022-03-01" set_default_newtab_experiment: set_default_browser_clicked: @@ -5762,11 +5319,12 @@ set_default_newtab_experiment: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18895 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-10-01" + expires: "2022-04-01" close_experiment_card_clicked: type: event description: | @@ -5776,11 +5334,12 @@ set_default_newtab_experiment: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/18895 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-10-01" + expires: "2022-04-01" set_default_setting_experiment: set_default_browser_clicked: @@ -5792,8 +5351,76 @@ set_default_setting_experiment: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/19047 - https://github.com/mozilla-mobile/fenix/pull/19924#issuecomment-861423789 + - https://github.com/mozilla-mobile/fenix/pull/21076#issuecomment-909237275 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-04-01" +start_on_home: + enter_home_screen: + type: event + description: | + Know how often user lands on Homescreen upon opening the app. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/19881 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/19885 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-06-16" + open_tabs_tray: + type: event + description: | + Know how often users open the tab tray. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/19881 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/19885 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-06-16" +recent_tabs: + show_all_clicked: + type: event + description: | + User has clicked show all button and opened tabs tray. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/19955 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/20138 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-06-23" + recent_tab_opened: + type: event + description: | + User has opened a recent tab from homescreen. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/19955 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/20138 + data_sensitivity: + - interaction + notification_emails: + - android-probes@mozilla.com + expires: "2022-06-23" + in_progress_media_tab_opened: + type: event + description: | + User has opened a recent tab from homescreen. + bugs: + - https://github.com/mozilla-mobile/fenix/issues/20393 + data_reviews: + - https://github.com/mozilla-mobile/fenix/pull/20138 data_sensitivity: - interaction notification_emails: - android-probes@mozilla.com - expires: "2021-10-01" + expires: "2022-06-23" diff --git a/app/proguard-android-optimize-3.5.0-modified.txt b/app/proguard-android-optimize-3.5.0-modified.txt deleted file mode 100644 index 0a64ad38a..000000000 --- a/app/proguard-android-optimize-3.5.0-modified.txt +++ /dev/null @@ -1,120 +0,0 @@ -#################################################################################################### -# Copy of `proguard-android-optimize.txt`. -# We need to _remove_ some of the configuration, so the only way to achieve that is to dump it here -# and modify it. -#################################################################################################### - -# This is a configuration file for ProGuard. -# http://proguard.sourceforge.net/index.html#manual/usage.html -# -# Starting with version 2.2 of the Android plugin for Gradle, this file is distributed together with -# the plugin and unpacked at build-time. The files in $ANDROID_HOME are no longer maintained and -# will be ignored by new version of the Android plugin for Gradle. - -# Optimizations: If you don't want to optimize, use the proguard-android.txt configuration file -# instead of this one, which turns off the optimization flags. -# Adding optimization introduces certain risks, since for example not all optimizations performed by -# ProGuard works on all versions of Dalvik. The following flags turn off various optimizations -# known to have issues, but the list may not be complete or up to date. (The "arithmetic" -# optimization can be used if you are only targeting Android 2.0 or later.) Make sure you test -# thoroughly if you go this route. --optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* --optimizationpasses 5 - --dontusemixedcaseclassnames --dontskipnonpubliclibraryclasses --verbose - -# Preserve some attributes that may be required for reflection. --keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod - --keep public class com.google.vending.licensing.ILicensingService --keep public class com.android.vending.licensing.ILicensingService --keep public class com.google.android.vending.licensing.ILicensingService --dontnote com.android.vending.licensing.ILicensingService --dontnote com.google.vending.licensing.ILicensingService --dontnote com.google.android.vending.licensing.ILicensingService - -# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native --keepclasseswithmembernames class * { - native ; -} - -# Keep setters in Views so that animations can still work. --keepclassmembers public class * extends android.view.View { - void set*(***); - *** get*(); -} - -# We want to keep methods in Activity that could be used in the XML attribute onClick. --keepclassmembers class * extends android.app.Activity { - public void *(android.view.View); -} - -# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations --keepclassmembers enum * { - public static **[] values(); - public static ** valueOf(java.lang.String); -} - --keepclassmembers class * implements android.os.Parcelable { - public static final ** CREATOR; -} - --keepclassmembers class **.R$* { - public static ; -} - -# Preserve annotated Javascript interface methods. --keepclassmembers class * { - @android.webkit.JavascriptInterface ; -} - -# The support libraries contains references to newer platform versions. -# Don't warn about those in case this app is linking against an older -# platform version. We know about them, and they are safe. --dontnote android.support.** --dontnote androidx.** --dontwarn android.support.** --dontwarn androidx.** - -# This class is deprecated, but remains for backward compatibility. --dontwarn android.util.FloatMath - -# Understand the @Keep support annotation. --keep class android.support.annotation.Keep --keep class androidx.annotation.Keep - --keep @android.support.annotation.Keep class * {*;} --keep @androidx.annotation.Keep class * {*;} - --keepclasseswithmembers class * { - @android.support.annotation.Keep ; -} - --keepclasseswithmembers class * { - @androidx.annotation.Keep ; -} - --keepclasseswithmembers class * { - @android.support.annotation.Keep ; -} - --keepclasseswithmembers class * { - @androidx.annotation.Keep ; -} - --keepclasseswithmembers class * { - @android.support.annotation.Keep (...); -} - --keepclasseswithmembers class * { - @androidx.annotation.Keep (...); -} - -# These classes are duplicated between android.jar and org.apache.http.legacy.jar. --dontnote org.apache.http.** --dontnote android.net.http.** - -# These classes are duplicated between android.jar and core-lambda-stubs.jar. --dontnote java.lang.invoke.** diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index b7a3d200f..e269f6577 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -32,17 +32,20 @@ -keep class org.mozilla.gecko.util.DebugConfig { *; } #################################################################################################### -# Force removal of slow Dispatchers.Main ServiceLoader +# kotlinx.coroutines: use the fast service loader to init MainDispatcherLoader by including a rule +# to rewrite this property to return true: +# https://github.com/Kotlin/kotlinx.coroutines/blob/8c98180f177bbe4b26f1ed9685a9280fea648b9c/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt#L19 +# +# R8 is expected to optimize the default implementation to avoid a performance issue but a bug in R8 +# as bundled with AGP v7.0.0 causes this optimization to fail so we use the fast service loader instead. See: +# https://github.com/mozilla-mobile/focus-android/issues/5102#issuecomment-897854121 +# +# The fast service loader appears to be as performant as the R8 optimization so it's not worth the +# churn to later remove this workaround. If needed, the upstream fix is being handled in +# https://issuetracker.google.com/issues/196302685 #################################################################################################### -# Allow R8 to optimize away the FastServiceLoader. -# Together with ServiceLoader optimization in R8 -# this results in direct instantiation when loading Dispatchers.Main -assumenosideeffects class kotlinx.coroutines.internal.MainDispatcherLoader { - boolean FAST_SERVICE_LOADER_ENABLED return false; -} - --assumenosideeffects class kotlinx.coroutines.internal.FastServiceLoader { - boolean ANDROID_DETECTED return true; + boolean FAST_SERVICE_LOADER_ENABLED return true; } #################################################################################################### diff --git a/app/src/androidTest/java/org/mozilla/fenix/AppRequestInterceptor.kt b/app/src/androidTest/java/org/mozilla/fenix/AppRequestInterceptor.kt index 59e2de9a5..8fefecae2 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/AppRequestInterceptor.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/AppRequestInterceptor.kt @@ -9,7 +9,7 @@ import androidx.navigation.NavController import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.request.RequestInterceptor import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ui.robots.appContext +import org.mozilla.fenix.helpers.TestHelper.appContext import java.lang.ref.WeakReference /** diff --git a/app/src/androidTest/java/org/mozilla/fenix/glean/BaselinePingTest.kt b/app/src/androidTest/java/org/mozilla/fenix/glean/BaselinePingTest.kt index fe941a86e..8d4a76464 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/glean/BaselinePingTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/glean/BaselinePingTest.kt @@ -11,6 +11,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.ActivityTestRule import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiSelector +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -76,10 +77,13 @@ class BaselinePingTest { companion object { @BeforeClass @JvmStatic + @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage fun setupOnce() { - val httpClient = ConceptFetchHttpUploader(lazy { - GeckoViewFetchClient(ApplicationProvider.getApplicationContext()) - }) + val httpClient = ConceptFetchHttpUploader( + lazy { + GeckoViewFetchClient(ApplicationProvider.getApplicationContext()) + } + ) // Fenix does not initialize the Glean SDK in tests/debug builds, but this test // requires Glean to be initialized so we need to do it manually. Additionally, @@ -149,8 +153,11 @@ class BaselinePingTest { // sending the ping that was submitted on background. This can go away once bug 1634375 // is fixed. device.pressRecentApps() - device.findObject(UiSelector().descriptionContains( - ApplicationProvider.getApplicationContext().getString(R.string.app_name))) + device.findObject( + UiSelector().descriptionContains( + ApplicationProvider.getApplicationContext().getString(R.string.app_name) + ) + ) .click() // Validate the received data. 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 70247901f..051dd9680 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/helpers/HomeActivityTestRule.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/HomeActivityTestRule.kt @@ -11,8 +11,8 @@ import androidx.test.rule.ActivityTestRule import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiSelector import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.helpers.TestHelper.appContext import org.mozilla.fenix.onboarding.FenixOnboarding -import org.mozilla.fenix.ui.robots.appContext /** * A [org.junit.Rule] to handle shared test set up for tests on [HomeActivity]. diff --git a/app/src/androidTest/java/org/mozilla/fenix/helpers/TestHelper.kt b/app/src/androidTest/java/org/mozilla/fenix/helpers/TestHelper.kt index 30db0383c..7df7d88cb 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/helpers/TestHelper.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/TestHelper.kt @@ -30,6 +30,7 @@ import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.Until import kotlinx.coroutines.runBlocking +import mozilla.components.support.ktx.android.content.appName import org.hamcrest.CoreMatchers import org.hamcrest.CoreMatchers.allOf import org.mozilla.fenix.R @@ -41,10 +42,13 @@ import java.io.File object TestHelper { - val packageName = InstrumentationRegistry.getInstrumentation().targetContext.packageName + val appContext: Context = InstrumentationRegistry.getInstrumentation().targetContext + val packageName: String = appContext.packageName + val appName = appContext.appName fun scrollToElementByText(text: String): UiScrollable { val appView = UiScrollable(UiSelector().scrollable(true)) + appView.waitForExists(waitingTime) appView.scrollTextIntoView(text) return appView } @@ -69,7 +73,7 @@ object TestHelper { editor.apply() } - fun restartApp(activity: HomeActivityTestRule) { + fun restartApp(activity: HomeActivityIntentTestRule) { with(activity) { finishActivity() mDevice.waitForIdle() @@ -79,7 +83,7 @@ object TestHelper { fun getPermissionAllowID(): String { return when - (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { true -> "com.android.permissioncontroller" false -> "com.android.packageinstaller" } diff --git a/app/src/androidTest/java/org/mozilla/fenix/helpers/ext/String.kt b/app/src/androidTest/java/org/mozilla/fenix/helpers/ext/String.kt index b754a8634..71b701679 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/helpers/ext/String.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/helpers/ext/String.kt @@ -16,10 +16,10 @@ import java.net.URISyntaxException */ fun String.removePrefixesIgnoreCase(vararg prefixes: String): String { var value = this - var lower = this.toLowerCase() + var lower = this.lowercase() prefixes.forEach { - if (lower.startsWith(it.toLowerCase())) { + if (lower.startsWith(it.lowercase())) { value = value.substring(it.length) lower = lower.substring(it.length) } diff --git a/app/src/androidTest/java/org/mozilla/fenix/perf/SampleBenchmark.kt b/app/src/androidTest/java/org/mozilla/fenix/perf/SampleBenchmark.kt new file mode 100644 index 000000000..f4da3d187 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/perf/SampleBenchmark.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 https://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.perf + +import androidx.benchmark.junit4.BenchmarkRule +import androidx.benchmark.junit4.measureRepeated +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** + * To run this benchmark: + * - Comment out @Ignore: DO NOT COMMIT THIS! + * - See run instructions in app/benchmark.gradle + * + * See https://developer.android.com/studio/profile/benchmark#write-benchmark for how to write a + * real benchmark, including testing UI code. See + * https://developer.android.com/studio/profile/benchmark#what-to-benchmark for when jetpack + * microbenchmark is a good fit. + */ +@Ignore("This is a sample: we don't want it to run when we run all the tests") +@RunWith(AndroidJUnit4::class) +class SampleBenchmark { + @get:Rule + val benchmarkRule = BenchmarkRule() + + @Test + fun additionBenchmark() = benchmarkRule.measureRepeated { + var i = 0 + while (i < 10_000_000) { + i++ + } + } +} diff --git a/app/src/androidTest/java/org/mozilla/fenix/perf/StartupExcessiveResourceUseTest.kt b/app/src/androidTest/java/org/mozilla/fenix/perf/StartupExcessiveResourceUseTest.kt index b40ad62b1..5640d9166 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/perf/StartupExcessiveResourceUseTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/perf/StartupExcessiveResourceUseTest.kt @@ -58,7 +58,7 @@ private val failureMsgRecyclerViewConstraintLayoutChildren = getErrorMessage( private val failureMsgNumberOfInflation = getErrorMessage( shortName = "Number of inflation on start up doesn't match expected count", implications = "The number of inflation can negatively impact start up time. Having more inflations" + - "will most likely mean we're adding extra work on the UI thread." + "will most likely mean we're adding extra work on the UI thread." ) /** * A performance test to limit the number of StrictMode suppressions and number of runBlocking used @@ -134,7 +134,7 @@ private fun countRecyclerViewConstraintLayoutChildren(view: View, parent: View?) return if (view !is ViewGroup) { viewValue } else { - viewValue + view.children.sumBy { countRecyclerViewConstraintLayoutChildren(it, view) } + viewValue + view.children.sumOf { countRecyclerViewConstraintLayoutChildren(it, view) } } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/screenshots/MenuScreenShotTest.kt b/app/src/androidTest/java/org/mozilla/fenix/screenshots/MenuScreenShotTest.kt index ab2465a79..342dcc14d 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/screenshots/MenuScreenShotTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/screenshots/MenuScreenShotTest.kt @@ -83,14 +83,14 @@ class MenuScreenShotTest : ScreenshotTest() { }.openLanguageSubMenu { Screengrab.screenshot("SettingsSubMenuAccessibilityRobot_settings-language") }.goBack { - // From about here we need to scroll up to ensure all settings options are visible. + // From about here we need to scroll up to ensure all settings options are visible. }.openSetDefaultBrowserSubMenu { Screengrab.screenshot("SettingsSubMenuDefaultBrowserRobot_settings-default-browser") }.goBack { - // Disabled for Pixel 2 - // }.openEnhancedTrackingProtectionSubMenu { - // Screengrab.screenshot("settings-enhanced-tp") - // }.goBack { + // Disabled for Pixel 2 + // }.openEnhancedTrackingProtectionSubMenu { + // Screengrab.screenshot("settings-enhanced-tp") + // }.goBack { }.openLoginsAndPasswordSubMenu { Screengrab.screenshot("SettingsSubMenuLoginsAndPasswords-settings-logins-passwords") }.goBack { @@ -176,7 +176,7 @@ class MenuScreenShotTest : ScreenshotTest() { @Test fun saveLoginPromptTest() { val saveLoginTest = - TestAssetHelper.getSaveLoginAsset(mockWebServer) + TestAssetHelper.getSaveLoginAsset(mockWebServer) navigationToolbar { }.enterURLAndEnterToBrowser(saveLoginTest.url) { verifySaveLoginPromptIsShownNotSave() diff --git a/app/src/androidTest/java/org/mozilla/fenix/syncintegration/SyncIntegrationTest.kt b/app/src/androidTest/java/org/mozilla/fenix/syncintegration/SyncIntegrationTest.kt index 2ab2cecca..9b8c973bf 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/syncintegration/SyncIntegrationTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/syncintegration/SyncIntegrationTest.kt @@ -173,9 +173,11 @@ class SyncIntegrationTest { // Useful functions for the tests fun typeEmail() { - val emailInput = mDevice.findObject(UiSelector() + val emailInput = mDevice.findObject( + UiSelector() .instance(0) - .className(EditText::class.java)) + .className(EditText::class.java) + ) emailInput.waitForExists(TestAssetHelper.waitingTime) val emailAddress = javaClass.classLoader!!.getResource("email.txt").readText() @@ -188,9 +190,11 @@ class SyncIntegrationTest { } fun typePassword() { - val passwordInput = mDevice.findObject(UiSelector() + val passwordInput = mDevice.findObject( + UiSelector() .instance(0) - .className(EditText::class.java)) + .className(EditText::class.java) + ) val passwordValue = javaClass.classLoader!!.getResource("password.txt").readText() passwordInput.setText(passwordValue) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt new file mode 100644 index 000000000..68af35c58 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt @@ -0,0 +1,262 @@ +/* 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.ui + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import okhttp3.mockwebserver.MockWebServer +import org.junit.After +import org.junit.Before +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test +import org.mozilla.fenix.helpers.AndroidAssetDispatcher +import org.mozilla.fenix.helpers.HomeActivityTestRule +import org.mozilla.fenix.helpers.TestAssetHelper.getGenericAsset +import org.mozilla.fenix.ui.robots.browserScreen +import org.mozilla.fenix.ui.robots.homeScreen +import org.mozilla.fenix.ui.robots.navigationToolbar +import org.mozilla.fenix.ui.robots.tabDrawer + +/** + * Tests for verifying basic functionality of tab collections + * + */ + +class CollectionTest { + /* ktlint-disable no-blank-line-before-rbrace */ + // This imposes unreadable grouping. + + private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + private lateinit var mockWebServer: MockWebServer + private val firstCollectionName = "testcollection_1" + private val secondCollectionName = "testcollection_2" + + @get:Rule + val activityTestRule = HomeActivityTestRule() + + @Before + fun setUp() { + mockWebServer = MockWebServer().apply { + dispatcher = AndroidAssetDispatcher() + start() + } + } + + @After + fun tearDown() { + mockWebServer.shutdown() + } + + @Test + // open a webpage, and add currently opened tab to existing collection + fun mainMenuSaveToExistingCollection() { + val firstWebPage = getGenericAsset(mockWebServer, 1) + val secondWebPage = getGenericAsset(mockWebServer, 2) + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openTabDrawer { + createCollection(firstWebPage.title, firstCollectionName) + verifySnackBarText("Collection saved!") + }.closeTabDrawer {} + + navigationToolbar { + }.enterURLAndEnterToBrowser(secondWebPage.url) { + verifyPageContent(secondWebPage.content) + }.openThreeDotMenu { + }.openSaveToCollection { + }.selectExistingCollection(firstCollectionName) { + verifySnackBarText("Tab saved!") + }.goToHomescreen { + }.expandCollection(firstCollectionName) { + verifyTabSavedInCollection(firstWebPage.title) + verifyTabSavedInCollection(secondWebPage.title) + } + } + + @Test + fun verifyAddTabButtonOfCollectionMenu() { + val firstWebPage = getGenericAsset(mockWebServer, 1) + val secondWebPage = getGenericAsset(mockWebServer, 2) + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openTabDrawer { + createCollection(firstWebPage.title, firstCollectionName) + verifySnackBarText("Collection saved!") + closeTab() + } + + navigationToolbar { + }.enterURLAndEnterToBrowser(secondWebPage.url) { + }.goToHomescreen { + }.expandCollection(firstCollectionName) { + clickCollectionThreeDotButton() + selectAddTabToCollection() + verifyTabsSelectedCounterText(1) + saveTabsSelectedForCollection() + verifySnackBarText("Tab saved!") + verifyTabSavedInCollection(secondWebPage.title) + } + } + + @Test + fun renameCollectionTest() { + val webPage = getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(webPage.url) { + }.openTabDrawer { + createCollection(webPage.title, firstCollectionName) + verifySnackBarText("Collection saved!") + }.closeTabDrawer { + }.goToHomescreen { + }.expandCollection(firstCollectionName) { + clickCollectionThreeDotButton() + selectRenameCollection() + }.typeCollectionNameAndSave("renamed_collection") {} + + homeScreen { + verifyCollectionIsDisplayed("renamed_collection") + } + } + + @Test + fun createSecondCollectionTest() { + val webPage = getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(webPage.url) { + }.openTabDrawer { + createCollection(webPage.title, firstCollectionName) + verifySnackBarText("Collection saved!") + createCollection(webPage.title, secondCollectionName, false) + verifySnackBarText("Collection saved!") + }.closeTabDrawer { + }.goToHomescreen {} + + homeScreen { + verifyCollectionIsDisplayed(firstCollectionName) + verifyCollectionIsDisplayed(secondCollectionName) + } + } + + @Test + fun removeTabFromCollectionTest() { + val webPage = getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(webPage.url) { + }.openTabDrawer { + createCollection(webPage.title, firstCollectionName) + verifySnackBarText("Collection saved!") + }.closeTabDrawer { + }.goToHomescreen { + }.expandCollection(firstCollectionName) { + removeTabFromCollection(webPage.title) + verifyTabSavedInCollection(webPage.title, false) + } + // To add this step when https://github.com/mozilla-mobile/fenix/issues/13177 is fixed +// homeScreen { +// verifyCollectionIsDisplayed(firstCollectionName, false) +// } + } + + @Test + @Ignore("To be fixed in https://github.com/mozilla-mobile/fenix/issues/20702") + fun swipeToRemoveTabFromCollectionTest() { + val firstWebPage = getGenericAsset(mockWebServer, 1) + val secondWebPage = getGenericAsset(mockWebServer, 2) + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openTabDrawer { + createCollection(firstWebPage.title, firstCollectionName) + verifySnackBarText("Collection saved!") + closeTab() + } + + navigationToolbar { + }.enterURLAndEnterToBrowser(secondWebPage.url) { + }.openThreeDotMenu { + }.openSaveToCollection { + }.selectExistingCollection(firstCollectionName) { + }.goToHomescreen {} + + homeScreen { + }.expandCollection(firstCollectionName) { + swipeCollectionItemLeft(firstWebPage.title) + verifyTabSavedInCollection(firstWebPage.title, false) + swipeCollectionItemRight(secondWebPage.title) + verifyTabSavedInCollection(secondWebPage.title, false) + } + // To add this step when https://github.com/mozilla-mobile/fenix/issues/13177 is fixed +// homeScreen { +// verifyCollectionIsDisplayed(firstCollectionName, false) +// } + } + + @Test + fun selectTabOnLongTapTest() { + val firstWebPage = getGenericAsset(mockWebServer, 1) + val secondWebPage = getGenericAsset(mockWebServer, 2) + + navigationToolbar { + }.enterURLAndEnterToBrowser(firstWebPage.url) { + }.openTabDrawer { + }.openNewTab { + }.submitQuery(secondWebPage.url.toString()) { + }.openTabDrawer { + longClickTab(firstWebPage.title) + verifyTabsMultiSelectionCounter(1) + selectTab(secondWebPage.title) + verifyTabsMultiSelectionCounter(2) + }.clickSaveCollection { + typeCollectionNameAndSave(firstCollectionName) + verifySnackBarText("Tabs saved!") + } + + tabDrawer { + }.closeTabDrawer { + }.goToHomescreen { + }.expandCollection(firstCollectionName) { + verifyTabSavedInCollection(firstWebPage.title) + verifyTabSavedInCollection(secondWebPage.title) + } + } + + @Test + fun navigateBackInCollectionFlowTest() { + val webPage = getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(webPage.url) { + }.openTabDrawer { + createCollection(webPage.title, firstCollectionName) + verifySnackBarText("Collection saved!") + }.closeTabDrawer { + }.openThreeDotMenu { + }.openSaveToCollection { + verifySelectCollectionScreen() + goBackInCollectionFlow() + } + + browserScreen { + }.openThreeDotMenu { + }.openSaveToCollection { + verifySelectCollectionScreen() + clickAddNewCollection() + verifyCollectionNameTextField() + goBackInCollectionFlow() + verifySelectCollectionScreen() + goBackInCollectionFlow() + } + // verify the browser layout is visible + browserScreen { + verifyMenuButton() + } + } +} diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt.ignore b/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt.ignore deleted file mode 100644 index 81b62c4f7..000000000 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/CollectionTest.kt.ignore +++ /dev/null @@ -1,266 +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.ui -// -//import androidx.test.espresso.NoMatchingViewException -//import androidx.test.platform.app.InstrumentationRegistry -//import androidx.test.uiautomator.By -//import androidx.test.uiautomator.UiDevice -//import androidx.test.uiautomator.Until -//import okhttp3.mockwebserver.MockWebServer -//import org.junit.After -//import org.junit.Before -//import org.junit.Ignore -//import org.junit.Rule -//import org.junit.Test -//import org.mozilla.fenix.helpers.AndroidAssetDispatcher -//import org.mozilla.fenix.helpers.HomeActivityTestRule -//import org.mozilla.fenix.helpers.TestAssetHelper -//import org.mozilla.fenix.ui.robots.homeScreen -//import org.mozilla.fenix.ui.robots.navigationToolbar -// -///** -// * Tests for verifying basic functionality of tab collection -// * -// */ -// -//class CollectionTest { -// /* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping. -// -// private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) -// private lateinit var mockWebServer: MockWebServer -// private val firstCollectionName = "testcollection_1" -// private val secondCollectionName = "testcollection_2" -// -// @get:Rule -// val activityTestRule = HomeActivityTestRule() -// -// @Before -// fun setUp() { -// mockWebServer = MockWebServer().apply { -// setDispatcher(AndroidAssetDispatcher()) -// start() -// } -// } -// -// @After -// fun tearDown() { -// mockWebServer.shutdown() -// } -// -// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587") -// @Test -// // open a webpage, and add currently opened tab to existing collection -// fun addTabToExistingCollectionTest() { -// val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) -// val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2) -// -// createCollection(firstCollectionName) -// -// homeScreen { -//// verifyExistingTabList() -// closeTab() -// }.openNavigationToolbar { -// }.enterURLAndEnterToBrowser(secondWebPage.url) { -// verifyPageContent(secondWebPage.content) -// }.openThreeDotMenu { -// clickBrowserViewSaveCollectionButton() -// }.selectExistingCollection(firstCollectionName) { -// verifySnackBarText("Tab saved!") -// }.openHomeScreen { -//// verifyExistingTabList() -// expandCollection(firstCollectionName) -// verifyItemInCollectionExists(firstWebPage.title) -// verifyItemInCollectionExists(secondWebPage.title) -// } -// } -// -// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587") -// @Test -// fun collectionMenuAddTabButtonTest() { -// val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2) -// -// createCollection(firstCollectionName) -// -// homeScreen { -// closeTab() -// }.openNavigationToolbar { -// }.enterURLAndEnterToBrowser(secondWebPage.url) { -// }.openHomeScreen { -// expandCollection(firstCollectionName) -// clickCollectionThreeDotButton() -// selectAddTabToCollection() -// verifyTabsSelectedCounterText(1) -// saveTabsSelectedForCollection() -// verifySnackBarText("Tab saved!") -// verifyItemInCollectionExists(secondWebPage.title) -// } -// } -// -// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587") -// @Test -// fun renameCollectionTest() { -// createCollection(firstCollectionName) -// -// homeScreen { -// // On homeview, tap the 3-dot button to expand, select rename, rename collection -// expandCollection(firstCollectionName) -// clickCollectionThreeDotButton() -// selectRenameCollection() -// typeCollectionName("renamed_collection") -// verifyCollectionIsDisplayed("renamed_collection") -// } -// } -// -// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587") -// @Test -// fun createCollectionFromTabTest() { -// val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) -// -// createCollection(firstCollectionName) -// homeScreen { -// }.openTabDrawer { -// verifyExistingOpenTabs(firstWebPage.title) -// }.openHomeScreen { -// try { -// verifyCollectionIsDisplayed(firstCollectionName) -// } catch (e: NoMatchingViewException) { -// scrollToElementByText(firstCollectionName) -// } -// } -// } -// -// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587") -// @Test -// fun removeTabFromCollectionTest() { -// val webPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) -// -// createCollection(firstCollectionName) -// homeScreen { -// closeTab() -// expandCollection(firstCollectionName) -// removeTabFromCollection(webPage.title) -// verifyItemInCollectionExists(webPage.title, false) -// } -// -// createCollection(firstCollectionName) -// homeScreen { -// closeTab() -// expandCollection(firstCollectionName) -// swipeCollectionItemLeft(webPage.title) -// verifyItemInCollectionExists(webPage.title, false) -// } -// -// createCollection(firstCollectionName) -// homeScreen { -// closeTab() -// expandCollection(firstCollectionName) -// swipeCollectionItemRight(webPage.title) -// verifyItemInCollectionExists(webPage.title, false) -// } -// } -// -// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587") -// @Test -// fun selectTabOnLongTapTest() { -// val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) -// val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2) -// -// navigationToolbar { -// }.enterURLAndEnterToBrowser(firstWebPage.url) { -// }.openHomeScreen { -// }.openNavigationToolbar { -// }.enterURLAndEnterToBrowser(secondWebPage.url) { -// }.openHomeScreen { -// longTapSelectTab(firstWebPage.title) -// verifySelectTabsView() -// verifyTabsSelectedCounterText(1) -// selectTabForCollection(secondWebPage.title) -// verifyTabsSelectedCounterText(2) -// saveTabsSelectedForCollection() -// typeCollectionName(firstCollectionName) -// verifySnackBarText("Tabs saved!") -//// closeTabViaXButton(firstWebPage.title) -//// closeTabViaXButton(secondWebPage.title) -// expandCollection(firstCollectionName) -// verifyItemInCollectionExists(firstWebPage.title) -// verifyItemInCollectionExists(secondWebPage.title) -// } -// } -// -// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587") -// @Test -// fun tabsOverflowMenuSaveCollectionTest() { -// val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) -// val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2) -// -// navigationToolbar { -// }.enterURLAndEnterToBrowser(firstWebPage.url) { -// }.openHomeScreen { -// }.openNavigationToolbar { -// }.enterURLAndEnterToBrowser(secondWebPage.url) { -// }.openHomeScreen { -// }.openTabsListThreeDotMenu { -// verifySaveCollection() -// }.clickOpenTabsMenuSaveCollection { -// verifySelectTabsView() -// verifyTabsSelectedCounterText(0) -// selectAllTabsForCollection() -// verifyTabsSelectedCounterText(2) -// saveTabsSelectedForCollection() -// typeCollectionName(firstCollectionName) -//// closeTabViaXButton(firstWebPage.title) -//// closeTabViaXButton(secondWebPage.title) -// expandCollection(firstCollectionName) -// verifyItemInCollectionExists(firstWebPage.title) -// verifyItemInCollectionExists(secondWebPage.title) -// } -// } -// -// @Ignore("Intermittent failures, see: https://github.com/mozilla-mobile/fenix/issues/10587") -// @Test -// fun navigateBackInCollectionFlowTest() { -// val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2) -// -// createCollection(firstCollectionName) -// navigationToolbar { -// }.enterURLAndEnterToBrowser(secondWebPage.url) { -// }.openHomeScreen { -// longTapSelectTab(secondWebPage.title) -// verifySelectTabsView() -// saveTabsSelectedForCollection() -// verifySelectCollectionView() -// clickAddNewCollection() -// verifyNameCollectionView() -// goBackCollectionFlow() -// verifySelectCollectionView() -// goBackCollectionFlow() -// verifySelectTabsView() -// goBackCollectionFlow() -// verifyHomeComponent() -// } -// } -// -// private fun createCollection(collectionName: String, firstCollection: Boolean = true) { -// val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) -// -// navigationToolbar { -// }.enterURLAndEnterToBrowser(firstWebPage.url) { -// verifyPageContent(firstWebPage.content) -// }.openThreeDotMenu { -// clickBrowserViewSaveCollectionButton() -// if (!firstCollection) -// clickAddNewCollection() -// -// }.typeCollectionName(collectionName) { -// verifySnackBarText("Tab saved!") -// }.openHomeScreen { -// mDevice.wait( -// Until.findObject(By.text(collectionName)), -// TestAssetHelper.waitingTime -// ) -// } -// } -//} diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/DeepLinkTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/DeepLinkTest.kt index ff9159210..d75c1a284 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/DeepLinkTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/DeepLinkTest.kt @@ -108,7 +108,7 @@ class DeepLinkTest { fun openSettingsLogins() { robot.openSettingsLogins { verifyDefaultView() - verifyDefaultValueAutofillLogins() + verifyDefaultValueAutofillLogins(InstrumentationRegistry.getInstrumentation().targetContext) } } 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 6dcdc9e4c..5c5fcbeb9 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt @@ -74,6 +74,8 @@ class HistoryTest { } @Test + // Test running on beta/release builds in CI: + // caution when making changes to it, so they don't block the builds fun visitedUrlHistoryTest() { val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt index 4afff92dd..c0efe42f7 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/NavigationToolbarTest.kt @@ -94,6 +94,8 @@ class NavigationToolbarTest { } @Test + // Test running on beta/release builds in CI: + // caution when making changes to it, so they don't block the builds fun visitURLTest() { val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/NoNetworkAccessStartupTests.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/NoNetworkAccessStartupTests.kt index 9373a195d..c7bcbb7ad 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/NoNetworkAccessStartupTests.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/NoNetworkAccessStartupTests.kt @@ -34,6 +34,8 @@ class NoNetworkAccessStartupTests { } @Test + // Test running on beta/release builds in CI: + // caution when making changes to it, so they don't block the builds // Based on STR from https://github.com/mozilla-mobile/fenix/issues/16886 fun noNetworkConnectionStartupTest() { setNetworkEnabled(false) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt index 9bd09b80b..ea683c369 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt @@ -96,7 +96,7 @@ class SettingsAddonsTest { acceptInstallAddon() verifyDownloadAddonPrompt(addonName, activityTestRule) - } + } } // Opens the addons settings menu, installs an addon, then uninstalls 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 39191b4ec..3681f6487 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsBasicsTest.kt @@ -161,7 +161,6 @@ class SettingsBasicsTest { } } - @Ignore("Failing, see: https://github.com/mozilla-mobile/fenix/issues/18986") @Test fun changeAccessibiltySettings() { // Goes through the settings and changes the default text on a webpage, then verifies if the text has changed. @@ -194,16 +193,4 @@ class SettingsBasicsTest { verifyMenuItemsAreDisabled() } } - - @Test - fun changeDefaultBrowserSetting() { - // Opens settings and toggles the default browser setting to on. The device settings open and allows the user to set a default browser. - homeScreen { - }.openThreeDotMenu { - }.openSettings { - verifyDefaultBrowserIsDisaled() - clickDefaultBrowserSwitch() - verifyAndroidDefaultAppsMenuAppears() - } - } } 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 1999e13d8..ebe3333d2 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt @@ -13,7 +13,7 @@ import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.mozilla.fenix.helpers.AndroidAssetDispatcher -import org.mozilla.fenix.helpers.HomeActivityTestRule +import org.mozilla.fenix.helpers.HomeActivityIntentTestRule import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestHelper import org.mozilla.fenix.helpers.TestHelper.openAppFromExternalLink @@ -36,7 +36,7 @@ class SettingsPrivacyTest { private val pageShortcutName = "TestShortcut" @get:Rule - val activityTestRule = HomeActivityTestRule() + val activityTestRule = HomeActivityIntentTestRule() @Before fun setUp() { @@ -176,7 +176,7 @@ class SettingsPrivacyTest { TestHelper.scrollToElementByText("Logins and passwords") }.openLoginsAndPasswordSubMenu { verifyDefaultView() - verifyDefaultValueAutofillLogins() + verifyDefaultValueAutofillLogins(InstrumentationRegistry.getInstrumentation().targetContext) verifyDefaultValueExceptions() }.openSavedLogins { verifySecurityPromptForLogins() @@ -270,22 +270,24 @@ class SettingsPrivacyTest { } @Test - @Ignore("See: https://github.com/mozilla-mobile/fenix/issues/10915") fun openExternalLinksInPrivateTest() { - val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) + val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2) setOpenLinksInPrivateOn() - openAppFromExternalLink(defaultWebPage.url.toString()) + openAppFromExternalLink(firstWebPage.url.toString()) browserScreen { }.openTabDrawer { verifyPrivateModeSelected() - }.openNewTab { }.dismissSearchBar { } + }.closeTabDrawer { + }.goToHomescreen { } setOpenLinksInPrivateOff() - openAppFromExternalLink(defaultWebPage.url.toString()) + // We need to open a different link, otherwise it will open the same session + openAppFromExternalLink(secondWebPage.url.toString()) browserScreen { }.openTabDrawer { @@ -294,7 +296,6 @@ class SettingsPrivacyTest { } @Test - @Ignore("See: https://github.com/mozilla-mobile/fenix/issues/10915") fun launchPageShortcutInPrivateModeTest() { val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -307,7 +308,18 @@ class SettingsPrivacyTest { addShortcutName(pageShortcutName) clickAddShortcutButton() clickAddAutomaticallyButton() - }.openHomeScreenShortcut(pageShortcutName) { + } + + mDevice.waitForIdle() + // We need to close the existing tab here, to open a different session + restartApp(activityTestRule) + browserScreen { + }.openTabDrawer { + closeTab() + } + + addToHomeScreen { + }.searchAndOpenHomeScreenShortcut(pageShortcutName) { }.openTabDrawer { verifyPrivateModeSelected() } @@ -328,8 +340,7 @@ class SettingsPrivacyTest { clickAddShortcutButton() clickAddAutomaticallyButton() }.openHomeScreenShortcut(pageShortcutName) { - }.openTabDrawer { - }.openNewTab { }.dismissSearchBar { } + }.goToHomescreen { } setOpenLinksInPrivateOff() restartApp(activityTestRule) @@ -339,8 +350,7 @@ class SettingsPrivacyTest { }.searchAndOpenHomeScreenShortcut(pageShortcutName) { }.openTabDrawer { verifyNormalModeSelected() - }.openNewTab { - }.dismissSearchBar { + }.closeTabDrawer { }.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 1e5d40593..de579e9da 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SmokeTest.kt @@ -20,23 +20,25 @@ import org.junit.Before import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.IntentReceiverActivity import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.helpers.AndroidAssetDispatcher -import org.mozilla.fenix.helpers.HomeActivityTestRule +import org.mozilla.fenix.helpers.HomeActivityIntentTestRule import org.mozilla.fenix.helpers.RecyclerViewIdlingResource import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestHelper +import org.mozilla.fenix.helpers.TestHelper.appName import org.mozilla.fenix.helpers.TestHelper.createCustomTabIntent import org.mozilla.fenix.helpers.TestHelper.deleteDownloadFromStorage +import org.mozilla.fenix.helpers.TestHelper.restartApp import org.mozilla.fenix.helpers.TestHelper.scrollToElementByText import org.mozilla.fenix.helpers.ViewVisibilityIdlingResource import org.mozilla.fenix.ui.robots.browserScreen import org.mozilla.fenix.ui.robots.clickTabCrashedRestoreButton import org.mozilla.fenix.ui.robots.clickUrlbar +import org.mozilla.fenix.ui.robots.collectionRobot import org.mozilla.fenix.ui.robots.customTabScreen import org.mozilla.fenix.ui.robots.dismissTrackingOnboarding import org.mozilla.fenix.ui.robots.downloadRobot @@ -44,7 +46,12 @@ import org.mozilla.fenix.ui.robots.enhancedTrackingProtection import org.mozilla.fenix.ui.robots.homeScreen import org.mozilla.fenix.ui.robots.navigationToolbar import org.mozilla.fenix.ui.robots.notificationShade +import org.mozilla.fenix.ui.robots.openEditURLView +import org.mozilla.fenix.ui.robots.searchScreen import org.mozilla.fenix.ui.robots.tabDrawer +import org.mozilla.fenix.ui.util.FRENCH_LANGUAGE_HEADER +import org.mozilla.fenix.ui.util.FRENCH_SYSTEM_LOCALE_OPTION +import org.mozilla.fenix.ui.util.ROMANIAN_LANGUAGE_HEADER import org.mozilla.fenix.ui.util.STRING_ONBOARDING_TRACKING_PROTECTION_HEADER /** @@ -63,6 +70,7 @@ class SmokeTest { private val downloadFileName = "Globe.svg" private val collectionName = "First Collection" private var bookmarksListIdlingResource: RecyclerViewIdlingResource? = null + private var localeListIdlingResource: RecyclerViewIdlingResource? = null private val customMenuItem = "TestMenuItem" // This finds the dialog fragment child of the homeFragment, otherwise the awesomeBar would return null @@ -75,7 +83,7 @@ class SmokeTest { } @get:Rule - val activityTestRule = HomeActivityTestRule() + val activityTestRule = HomeActivityIntentTestRule() private lateinit var browserStore: BrowserStore @get: Rule @@ -130,6 +138,10 @@ class SmokeTest { if (readerViewNotification != null) { IdlingRegistry.getInstance().unregister(readerViewNotification) } + + if (localeListIdlingResource != null) { + IdlingRegistry.getInstance().unregister(localeListIdlingResource) + } } // Verifies the first run onboarding screen @@ -249,23 +261,16 @@ class SmokeTest { // Verifies the Synced tabs menu or Sync Sign In menu opens from a tab's 3 dot menu. // The test is assuming we are NOT signed in. fun openMainMenuSyncItemTest() { - if (FeatureFlags.tabsTrayRewrite) { - homeScreen { - }.openThreeDotMenu { - }.openSyncSignIn { - verifySyncSignInMenuHeader() - } - } else { - homeScreen { - }.openThreeDotMenu { - }.openSyncedTabs { - verifySyncedTabsMenuHeader() - } + homeScreen { + }.openThreeDotMenu { + }.openSyncSignIn { + verifySyncSignInMenuHeader() } } - // Could be removed when more smoke tests from the Settings category are added @Test + // Test running on beta/release builds in CI: + // caution when making changes to it, so they don't block the builds // Verifies the Settings menu opens from a tab's 3 dot menu fun openMainMenuSettingsItemTest() { homeScreen { @@ -414,9 +419,9 @@ class SmokeTest { } } - @Ignore("Failing, see https://github.com/mozilla-mobile/fenix/issues/18647") @Test fun customTrackingProtectionSettingsTest() { + val genericWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) val trackingPage = TestAssetHelper.getEnhancedTrackingProtectionAsset(mockWebServer) homeScreen { @@ -429,10 +434,14 @@ class SmokeTest { }.goBackToHomeScreen {} navigationToolbar { - }.openTrackingProtectionTestPage(trackingPage.url, true) {} + // browsing a basic page to allow GV to load on a fresh run + }.enterURLAndEnterToBrowser(genericWebPage.url) { + }.openNavigationToolbar { + }.openTrackingProtectionTestPage(trackingPage.url, true) { + dismissTrackingOnboarding() + } enhancedTrackingProtection { - dismissTrackingOnboarding() }.openEnhancedTrackingProtectionSheet { verifyTrackingCookiesBlocked() verifyCryptominersBlocked() @@ -511,6 +520,8 @@ class SmokeTest { } @Test + // Test running on beta/release builds in CI: + // caution when making changes to it, so they don't block the builds // Goes through the settings and changes the search suggestion toggle, then verifies it changes. fun toggleSearchSuggestions() { @@ -564,6 +575,7 @@ class SmokeTest { @Test // Saves a login, then changes it and verifies the update + @Ignore("To be fixed in https://github.com/mozilla-mobile/fenix/issues/20702") fun updateSavedLoginTest() { val saveLoginTest = TestAssetHelper.getSaveLoginAsset(mockWebServer) @@ -590,7 +602,7 @@ class SmokeTest { verifySavedLoginFromPrompt() viewSavedLoginDetails() revealPassword() - verifyPasswordSaved("test") + verifyPasswordSaved("test") // failing here locally } } @@ -655,12 +667,6 @@ class SmokeTest { enhancedTrackingProtection { verifyEnhancedTrackingProtectionNotice() }.closeNotificationPopup {} - - browserScreen { - }.openThreeDotMenu { - }.openReportSiteIssue { - verifyUrl("webcompat.com/issues/new") - } } @Test @@ -900,10 +906,13 @@ class SmokeTest { mDevice.waitForIdle() }.goToHomescreen { }.clickSaveTabsToCollectionButton { - selectTab(firstWebPage.title) + longClickTab(firstWebPage.title) selectTab(secondWebPage.title) - clickSaveCollection() - typeCollectionName(collectionName) + }.clickSaveCollection { + typeCollectionNameAndSave(collectionName) + } + + tabDrawer { verifySnackBarText("Collection saved!") snackBarButtonClick("VIEW") } @@ -914,7 +923,6 @@ class SmokeTest { } } - @Ignore("Disabling until re-implemented by #19090") @Test fun verifyExpandedCollectionItemsTest() { val webPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -929,14 +937,16 @@ class SmokeTest { homeScreen { verifyCollectionIsDisplayed(collectionName) verifyCollectionIcon() - expandCollection(collectionName) + }.expandCollection(collectionName) { verifyTabSavedInCollection(webPage.title) verifyCollectionTabLogo() verifyCollectionTabUrl() verifyShareCollectionButtonIsVisible(true) verifyCollectionMenuIsVisible(true) verifyCollectionItemRemoveButtonIsVisible(webPage.title, true) - collapseCollection(collectionName) + }.collapseCollection(collectionName) {} + + collectionRobot { verifyTabSavedInCollection(webPage.title, false) verifyShareCollectionButtonIsVisible(false) verifyCollectionMenuIsVisible(false) @@ -951,11 +961,12 @@ class SmokeTest { }.enterURLAndEnterToBrowser(webPage.url) { }.openTabDrawer { createCollection(webPage.title, collectionName) + verifySnackBarText("Collection saved!") closeTab() } - browserScreen { - }.goToHomescreen { - expandCollection(collectionName) + + homeScreen { + }.expandCollection(collectionName) { clickCollectionThreeDotButton() selectOpenTabs() } @@ -964,7 +975,6 @@ class SmokeTest { } } - @Ignore("Disabling until re-implemented by #19090") @Test fun shareCollectionTest() { val webPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -975,15 +985,20 @@ class SmokeTest { createCollection(webPage.title, collectionName) snackBarButtonClick("VIEW") } + homeScreen { - expandCollection(collectionName) + }.expandCollection(collectionName) { clickShareCollectionButton() + } + + homeScreen { verifyShareTabsOverlay() } } - @Ignore("Disabling until re-implemented by #19090") @Test + // Test running on beta/release builds in CI: + // caution when making changes to it, so they don't block the builds fun deleteCollectionTest() { val webPage = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -993,11 +1008,15 @@ class SmokeTest { createCollection(webPage.title, collectionName) snackBarButtonClick("VIEW") } + homeScreen { - expandCollection(collectionName) + }.expandCollection(collectionName) { clickCollectionThreeDotButton() selectDeleteCollection() confirmDeleteCollection() + } + + homeScreen { verifyNoCollectionsText() } } @@ -1098,13 +1117,14 @@ class SmokeTest { verifyExistingOpenTabs("Test_Page_1") verifyCloseTabsButton("Test_Page_1") verifyOpenedTabThumbnail() - verifyBrowserTabsTrayURL("localhost") verifyTabTrayOverflowMenu(true) verifyNewTabButton() } } @Test + // Test running on beta/release builds in CI: + // caution when making changes to it, so they don't block the builds fun noHistoryInPrivateBrowsingTest() { val website = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -1132,11 +1152,16 @@ class SmokeTest { verifyAddPrivateBrowsingShortcutButton() clickAddPrivateBrowsingShortcutButton() clickAddAutomaticallyButton() - }.openHomeScreenShortcut("Private Firefox Preview") { + }.openHomeScreenShortcut("Private $appName") {} + searchScreen { + verifySearchView() + }.dismissSearchBar { + verifyPrivateSessionMessage() } } @Test + @Ignore("To be re-enabled later. See https://github.com/mozilla-mobile/fenix/issues/20716") fun mainMenuInstallPWATest() { val pwaPage = "https://rpappalax.github.io/testapp/" @@ -1206,6 +1231,7 @@ class SmokeTest { } } + @Ignore("Test failure caused by: https://github.com/mozilla-mobile/fenix/issues/19964") @Test fun restoreTabCrashedReporterTest() { val website = TestAssetHelper.getGenericAsset(mockWebServer, 1) @@ -1234,9 +1260,7 @@ class SmokeTest { ) customTabScreen { - browserScreen { - verifyPageContent(customTabPage.content) - } + verifyCustomTabCloseButton() }.openMainMenu { verifyPoweredByTextIsDisplayed() verifyCustomMenuItem(customMenuItem) @@ -1261,9 +1285,7 @@ class SmokeTest { ) customTabScreen { - browserScreen { - verifyPageContent(customTabPage.content) - } + verifyCustomTabCloseButton() }.openMainMenu { }.clickOpenInBrowserButton { verifyTabCounter("1") @@ -1320,4 +1342,174 @@ class SmokeTest { assertPlaybackState(browserStore, MediaSession.PlaybackState.PAUSED) } } + + @Test + // For API>23 + // Verifies the default browser switch opens the system default apps menu. + fun changeDefaultBrowserSetting() { + homeScreen { + }.openThreeDotMenu { + }.openSettings { + verifyDefaultBrowserIsDisaled() + clickDefaultBrowserSwitch() + verifyAndroidDefaultAppsMenuAppears() + } + } + + @Test + fun copyTextTest() { + val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(genericURL.url) { + longClickAndCopyText("content") + }.openNavigationToolbar { + openEditURLView() + } + + searchScreen { + clickClearButton() + longClickToolbar() + clickPasteText() + verifyPastedToolbarText("content") + } + } + + @Test + fun selectAllAndCopyTextTest() { + val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(genericURL.url) { + longClickAndCopyText("content", true) + }.openNavigationToolbar { + openEditURLView() + } + + searchScreen { + clickClearButton() + longClickToolbar() + clickPasteText() + verifyPastedToolbarText("Page content: 1") + } + } + + @Test + fun switchLanguageTest() { + homeScreen { + }.openThreeDotMenu { + }.openSettings { + }.openLanguageSubMenu { + localeListIdlingResource = + RecyclerViewIdlingResource( + activityTestRule.activity.findViewById(R.id.locale_list), + 2 + ) + IdlingRegistry.getInstance().register(localeListIdlingResource) + selectLanguage("Romanian") + verifyLanguageHeaderIsTranslated(ROMANIAN_LANGUAGE_HEADER) + selectLanguage("Français") + verifyLanguageHeaderIsTranslated(FRENCH_LANGUAGE_HEADER) + selectLanguage(FRENCH_SYSTEM_LOCALE_OPTION) + verifyLanguageHeaderIsTranslated("Language") + IdlingRegistry.getInstance().unregister(localeListIdlingResource) + } + } + + @Test + fun goToHomeScreenBottomToolbarTest() { + val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(genericURL.url) { + mDevice.waitForIdle() + }.goToHomescreen { + verifyHomeScreen() + } + } + + @Test + fun goToHomeScreenTopToolbarTest() { + val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + homeScreen { + }.openThreeDotMenu { + }.openSettings { + }.openCustomizeSubMenu { + clickTopToolbarToggle() + }.goBack { + }.goBack { + }.openNavigationToolbar { + }.enterURLAndEnterToBrowser(genericURL.url) { + mDevice.waitForIdle() + }.goToHomescreen { + verifyHomeScreen() + } + } + + @Test + fun goToHomeScreenBottomToolbarPrivateModeTest() { + val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + homeScreen { + togglePrivateBrowsingModeOnOff() + } + + navigationToolbar { + }.enterURLAndEnterToBrowser(genericURL.url) { + mDevice.waitForIdle() + }.goToHomescreen { + verifyHomeScreen() + } + } + + @Test + fun goToHomeScreenTopToolbarPrivateModeTest() { + val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + homeScreen { + togglePrivateBrowsingModeOnOff() + }.openThreeDotMenu { + }.openSettings { + }.openCustomizeSubMenu { + clickTopToolbarToggle() + }.goBack { + }.goBack { + }.openNavigationToolbar { + }.enterURLAndEnterToBrowser(genericURL.url) { + mDevice.waitForIdle() + }.goToHomescreen { + verifyHomeScreen() + } + } + + @Test + fun startOnHomeSettingsMenuItemsTest() { + homeScreen { + }.openThreeDotMenu { + }.openSettings { + }.openTabsSubMenu { + verifyStartOnHomeOptions() + } + } + + @Test + fun alwaysStartOnHomeTest() { + val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1) + + navigationToolbar { + }.enterURLAndEnterToBrowser(genericURL.url) { + mDevice.waitForIdle() + }.openThreeDotMenu { + }.openSettings { + }.openTabsSubMenu { + clickAlwaysStartOnHomeToggle() + } + + restartApp(activityTestRule) + + homeScreen { + verifyHomeScreen() + } + } } 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 69b2f294a..a992796f6 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/ThreeDotMenuMainTest.kt @@ -9,7 +9,6 @@ import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test -import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.helpers.AndroidAssetDispatcher import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.ui.robots.homeScreen @@ -49,11 +48,7 @@ class ThreeDotMenuMainTest { verifyHistoryButton() verifyDownloadsButton() verifyAddOnsButton() - if (FeatureFlags.tabsTrayRewrite) { - verifySyncSignInButton() - } else { - verifySyncedTabsButton() - } + verifySyncSignInButton() verifyDesktopSite() verifyWhatsNewButton() verifyHelpButton() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/AccountSettingsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/AccountSettingsRobot.kt index 62e8ca844..a20e0941e 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/AccountSettingsRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/AccountSettingsRobot.kt @@ -44,19 +44,19 @@ private fun deviceName() = Espresso.onView(CoreMatchers.allOf(ViewMatchers.withT private fun disconnectButton() = Espresso.onView(CoreMatchers.allOf(ViewMatchers.withId(R.id.signOutDisconnect))) private fun assertBookmarksCheckbox() = bookmarksCheckbox().check( - ViewAssertions.matches( - ViewMatchers.withEffectiveVisibility( - ViewMatchers.Visibility.VISIBLE - ) + ViewAssertions.matches( + ViewMatchers.withEffectiveVisibility( + ViewMatchers.Visibility.VISIBLE ) + ) ) private fun assertHistoryCheckbox() = historyCheckbox().check( - ViewAssertions.matches( - ViewMatchers.withEffectiveVisibility( - ViewMatchers.Visibility.VISIBLE - ) + ViewAssertions.matches( + ViewMatchers.withEffectiveVisibility( + ViewMatchers.Visibility.VISIBLE ) + ) ) private fun assertSignOutButton() = signOutButton().check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/AddToHomeScreenRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/AddToHomeScreenRobot.kt index 7a7219ef7..859bd317b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/AddToHomeScreenRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/AddToHomeScreenRobot.kt @@ -91,8 +91,12 @@ fun addToHomeScreen(interact: AddToHomeScreenRobot.() -> Unit): AddToHomeScreenR private fun shortcutNameField() = onView(withId(R.id.shortcut_text)) private fun assertShortcutNameField(expectedText: String) { - onView(allOf(withId(R.id.shortcut_text), - withText(expectedText))) + onView( + allOf( + withId(R.id.shortcut_text), + withText(expectedText) + ) + ) .check(matches(isCompletelyDisplayed())) } 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 3f15b840e..6a222b947 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 @@ -105,8 +105,10 @@ class BookmarksRobot { fun verifySelectDefaultFolderSnackBarText() = assertSnackBarText("Can’t edit default folders") fun verifyCurrentFolderTitle(title: String) { - mDevice.findObject(UiSelector().resourceId("$packageName:id/navigationToolbar") - .textContains(title)) + mDevice.findObject( + UiSelector().resourceId("$packageName:id/navigationToolbar") + .textContains(title) + ) .waitForExists(waitingTime) onView( @@ -119,8 +121,10 @@ class BookmarksRobot { } fun waitForBookmarksFolderContentToExist(parentFolderName: String, childFolderName: String) { - mDevice.findObject(UiSelector().resourceId("$packageName:id/navigationToolbar") - .textContains(parentFolderName)) + mDevice.findObject( + UiSelector().resourceId("$packageName:id/navigationToolbar") + .textContains(parentFolderName) + ) .waitForExists(waitingTime) mDevice.waitNotNull(Until.findObject(By.text(childFolderName)), waitingTime) 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 30a1691bf..0390756d8 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 @@ -2,7 +2,7 @@ * 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/. */ -@file:Suppress("TooManyFunctions") +@file:Suppress("TooManyFunctions", "TooGenericExceptionCaught") package org.mozilla.fenix.ui.robots @@ -23,6 +23,7 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.Visibility import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +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.withResourceName @@ -333,6 +334,59 @@ class BrowserRobot { element.click(LONG_CLICK_DURATION) } + fun longClickAndCopyText(expectedText: String, selectAll: Boolean = false) { + try { + // Long click desired text + mDevice.waitForWindowUpdate(packageName, waitingTime) + mDevice.findObject(UiSelector().resourceId("$packageName:id/engineView")) + .waitForExists(waitingTime) + mDevice.findObject(UiSelector().textContains(expectedText)).waitForExists(waitingTime) + val link = mDevice.findObject(By.textContains(expectedText)) + link.click(LONG_CLICK_DURATION) + + // Click Select all from the text selection toolbar + if (selectAll) { + mDevice.findObject(UiSelector().textContains("Select all")).waitForExists(waitingTime) + val selectAllText = mDevice.findObject(By.textContains("Select all")) + selectAllText.click() + } + + // Click Copy from the text selection toolbar + mDevice.findObject(UiSelector().textContains("Copy")).waitForExists(waitingTime) + val copyText = mDevice.findObject(By.textContains("Copy")) + copyText.click() + } catch (e: NullPointerException) { + println("Failed to long click desired text: ${e.localizedMessage}") + + // Refresh the page in case the first long click didn't succeed + navigationToolbar { + }.openThreeDotMenu { + }.refreshPage { + mDevice.waitForIdle() + } + + // Long click again the desired text + mDevice.waitForWindowUpdate(packageName, waitingTime) + mDevice.findObject(UiSelector().resourceId("$packageName:id/engineView")) + .waitForExists(waitingTime) + mDevice.findObject(UiSelector().textContains(expectedText)).waitForExists(waitingTime) + val link = mDevice.findObject(By.textContains(expectedText)) + link.click(LONG_CLICK_DURATION) + + // Click again Select all from the text selection toolbar + if (selectAll) { + mDevice.findObject(UiSelector().textContains("Select all")).waitForExists(waitingTime) + val selectAllText = mDevice.findObject(By.textContains("Select all")) + selectAllText.click() + } + + // Click again Copy from the text selection toolbar + mDevice.findObject(UiSelector().textContains("Copy")).waitForExists(waitingTime) + val copyText = mDevice.findObject(By.textContains("Copy")) + copyText.click() + } + } + fun snackBarButtonClick(expectedText: String) { onView(allOf(withId(R.id.snackbar_btn), withText(expectedText))).check( matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)) @@ -455,8 +509,10 @@ class BrowserRobot { fun openTabDrawer(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition { mDevice.waitForIdle(waitingTime) tabsCounter().click() - mDevice.waitNotNull(Until.findObject(By.res("$packageName:id/tab_layout")), - waitingTime) + mDevice.waitNotNull( + Until.findObject(By.res("$packageName:id/tab_layout")), + waitingTime + ) TabDrawerRobot().interact() return TabDrawerRobot.Transition() @@ -481,9 +537,9 @@ class BrowserRobot { } fun goToHomescreen(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition { - openTabDrawer { - }.openNewTab { - }.dismissSearchBar {} + onView(withContentDescription("Home screen")) + .check(matches(isDisplayed())) + .click() HomeScreenRobot().interact() return HomeScreenRobot.Transition() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/CollectionRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/CollectionRobot.kt new file mode 100644 index 000000000..cfc612565 --- /dev/null +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/CollectionRobot.kt @@ -0,0 +1,275 @@ +package org.mozilla.fenix.ui.robots + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.NoMatchingViewException +import androidx.test.espresso.action.ViewActions.pressImeActionButton +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.action.ViewActions.swipeLeft +import androidx.test.espresso.action.ViewActions.swipeRight +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.hasSibling +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiSelector +import androidx.test.uiautomator.Until +import org.hamcrest.Matchers.allOf +import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime +import org.mozilla.fenix.helpers.TestHelper.packageName +import org.mozilla.fenix.helpers.TestHelper.scrollToElementByText +import org.mozilla.fenix.helpers.click +import org.mozilla.fenix.helpers.ext.waitNotNull + +class CollectionRobot { + + fun verifySelectCollectionScreen() { + onView(withText("Select collection")) + .check(matches(isDisplayed())) + + onView(withId(R.id.collections_list)) + .check(matches(isDisplayed())) + + onView(withText("Add new collection")) + .check(matches(isDisplayed())) + } + + fun clickAddNewCollection() = addNewCollectionButton().click() + + fun verifyCollectionNameTextField() { + mainMenuEditCollectionNameField().check(matches(isDisplayed())) + } + + // names a collection saved from tab drawer + fun typeCollectionNameAndSave(collectionName: String) { + collectionNameTextField().perform(replaceText(collectionName)) + mDevice.findObject(UiSelector().textContains("OK")).click() + } + + fun verifyTabsSelectedCounterText(numOfTabs: Int) { + mDevice.findObject(UiSelector().text("Select tabs to save")) + .waitUntilGone(waitingTime) + + val tabsCounter = onView(withId(R.id.bottom_bar_text)) + when (numOfTabs) { + 1 -> tabsCounter.check(matches(withText("$numOfTabs tab selected"))) + 2 -> tabsCounter.check(matches(withText("$numOfTabs tabs selected"))) + } + } + + fun saveTabsSelectedForCollection() { + onView(withId(R.id.save_button)).click() + } + + fun verifyTabSavedInCollection(title: String, visible: Boolean = true) { + if (visible) { + scrollToElementByText(title) + collectionItem(title) + .check( + matches(isDisplayed()) + ) + } else + collectionItem(title) + .check(doesNotExist()) + } + + fun verifyCollectionTabUrl() { + onView(withId(R.id.caption)).check(matches(isDisplayed())) + } + + fun verifyCollectionTabLogo() { + onView(withId(R.id.favicon)).check(matches(isDisplayed())) + } + + fun verifyShareCollectionButtonIsVisible(visible: Boolean) { + shareCollectionButton() + .check( + if (visible) matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)) + else matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)) + ) + } + + fun clickShareCollectionButton() = onView(withId(R.id.collection_share_button)).click() + + fun verifyCollectionMenuIsVisible(visible: Boolean) { + collectionThreeDotButton() + .check( + if (visible) matches( + withEffectiveVisibility( + ViewMatchers.Visibility.VISIBLE + ) + ) + else matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)) + ) + } + + fun clickCollectionThreeDotButton() { + collectionThreeDotButton().click() + mDevice.waitNotNull( + Until.findObject(By.text("Delete collection")), + waitingTime + ) + } + + fun selectOpenTabs() { + onView(withText("Open tabs")).click() + } + + fun selectRenameCollection() { + onView(withText("Rename collection")).click() + mDevice.waitNotNull(Until.findObject(By.text("Rename collection"))) + } + + fun selectAddTabToCollection() { + onView(withText("Add tab")).click() + mDevice.waitNotNull(Until.findObject(By.text("Select Tabs"))) + } + + fun selectDeleteCollection() { + onView(withText("Delete collection")).click() + mDevice.waitNotNull(Until.findObject(By.res("android:id/message")), waitingTime) + } + + fun confirmDeleteCollection() { + onView(withText("DELETE")).click() + mDevice.waitNotNull( + Until.findObject(By.res("$packageName:id/no_collections_header")), + waitingTime + ) + } + + fun verifyCollectionItemRemoveButtonIsVisible(title: String, visible: Boolean) { + removeTabFromCollectionButton(title) + .check( + if (visible) matches( + withEffectiveVisibility( + ViewMatchers.Visibility.VISIBLE + ) + ) + else doesNotExist() + ) + } + + fun removeTabFromCollection(title: String) = removeTabFromCollectionButton(title).click() + + fun swipeCollectionItemRight(title: String) { + scrollToElementByText(title) + // Swipping can sometimes fail to remove the tab, so if the tab still exists, we need to repeat it + var retries = 0 // number of retries before failing, will stop at 2 + while (mDevice.findObject( + UiSelector() + .resourceId("$packageName:id/label") + .text(title) + ).exists() && retries < 2 + ) { + collectionItem(title).perform(swipeRight()) + retries++ + } + } + + fun swipeCollectionItemLeft(title: String) { + scrollToElementByText(title) + // Swipping can sometimes fail to remove the tab, so if the tab still exists, we need to repeat it + var retries = 0 // number of retries before failing, will stop at 2 + while (mDevice.findObject( + UiSelector() + .resourceId("$packageName:id/label") + .text(title) + ).exists() && retries < 2 + ) { + collectionItem(title).perform(swipeLeft()) + retries++ + } + } + + fun verifySnackBarText(expectedText: String) { + mDevice.findObject(UiSelector().text(expectedText)).waitForExists(waitingTime) + } + + fun goBackInCollectionFlow() = backButton().click() + + class Transition { + fun collapseCollection( + title: String, + interact: HomeScreenRobot.() -> Unit + ): HomeScreenRobot.Transition { + try { + mDevice.waitNotNull(Until.findObject(By.text(title)), waitingTime) + onView(allOf(withId(R.id.chevron), hasSibling(withText(title)))).click() + } catch (e: NoMatchingViewException) { + scrollToElementByText(title) + } + + HomeScreenRobot().interact() + return HomeScreenRobot.Transition() + } + + // names a collection saved from the 3dot menu + fun typeCollectionNameAndSave( + name: String, + interact: BrowserRobot.() -> Unit + ): BrowserRobot.Transition { + mDevice.findObject(UiSelector().resourceId("$packageName:id/name_collection_edittext")) + .waitForExists(waitingTime) + + mainMenuEditCollectionNameField().perform( + replaceText(name), + pressImeActionButton() + ) + + // wait for the collection creation wrapper to be dismissed + mDevice.waitNotNull(Until.gone(By.res("$packageName:id/createCollectionWrapper"))) + + BrowserRobot().interact() + return BrowserRobot.Transition() + } + + fun selectExistingCollection( + title: String, + interact: BrowserRobot.() -> Unit + ): BrowserRobot.Transition { + mDevice.waitNotNull(Until.findObject(By.text(title)), waitingTime) + onView(withText(title)).click() + + BrowserRobot().interact() + return BrowserRobot.Transition() + } + } +} + +fun collectionRobot(interact: CollectionRobot.() -> Unit): CollectionRobot.Transition { + CollectionRobot().interact() + return CollectionRobot.Transition() +} + +private fun collectionThreeDotButton() = + onView(withId(R.id.collection_overflow_button)) + +private fun collectionItem(title: String) = + onView(allOf(withId(R.id.label), withText(title))) + +private fun shareCollectionButton() = onView(withId(R.id.collection_share_button)) + +private fun removeTabFromCollectionButton(title: String) = + onView( + allOf( + withId(R.id.secondary_button), + hasSibling(withText(title)) + ) + ) + +// collection name text field, opened from tab drawer +private fun collectionNameTextField() = onView(withId(R.id.collection_name)) + +// collection name text field, opened from main menu +private fun mainMenuEditCollectionNameField() = + onView(withId(R.id.name_collection_edittext)) + +private fun addNewCollectionButton() = onView(withText("Add new collection")) + +private fun backButton() = + onView(withId(R.id.back_button)) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/CustomTabRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/CustomTabRobot.kt index 90f05e96a..0ff73501f 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/CustomTabRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/CustomTabRobot.kt @@ -7,13 +7,14 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.uiautomator.UiSelector import junit.framework.TestCase.assertTrue -import mozilla.components.support.ktx.android.content.appName import org.mozilla.fenix.R import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime +import org.mozilla.fenix.helpers.TestHelper.appName /** * Implementation of the robot pattern for Custom tabs @@ -29,7 +30,10 @@ class CustomTabRobot { } fun verifyPoweredByTextIsDisplayed() { - mDevice.findObject(UiSelector().textContains("POWERED BY ${appContext.appName}")) + assertTrue( + mDevice.findObject(UiSelector().textContains("POWERED BY $appName")) + .waitForExists(waitingTime) + ) } fun verifyOpenInBrowserButtonExists() { @@ -43,7 +47,11 @@ class CustomTabRobot { fun verifyRefreshButtonExists() = assertTrue(refreshButton().waitForExists(waitingTime)) fun verifyCustomMenuItem(label: String) { - assertTrue(mDevice.findObject(UiSelector().text(label)).exists()) + assertTrue(mDevice.findObject(UiSelector().text(label)).waitForExists(waitingTime)) + } + + fun verifyCustomTabCloseButton() { + closeButton().check(matches(isDisplayed())) } class Transition { @@ -75,10 +83,12 @@ private fun desktopSiteButton() = onView(withId(R.id.switch_widget)) private fun findInPageButton() = onView(withText("Find in page")) -private fun openInBrowserButton() = onView(withText("Open in ${appContext.appName}")) +private fun openInBrowserButton() = onView(withText("Open in $appName")) private fun refreshButton() = mDevice.findObject(UiSelector().description("Refresh")) private fun forwardButton() = mDevice.findObject(UiSelector().description("Forward")) private fun backButton() = mDevice.findObject(UiSelector().description("Back")) + +private fun closeButton() = onView(withContentDescription("Return to previous app")) 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 aca0e0bf0..b2fdff296 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 @@ -12,10 +12,10 @@ import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.matcher.IntentMatchers import androidx.test.espresso.matcher.RootMatchers.isDialog -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withContentDescription +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice @@ -24,13 +24,13 @@ import androidx.test.uiautomator.Until import org.hamcrest.CoreMatchers import org.junit.Assert.assertTrue import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.Constants.PackageName.GOOGLE_APPS_PHOTOS import org.mozilla.fenix.helpers.TestAssetHelper +import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime import org.mozilla.fenix.helpers.TestHelper +import org.mozilla.fenix.helpers.TestHelper.packageName import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.ext.waitNotNull -import org.mozilla.fenix.helpers.Constants.PackageName.GOOGLE_APPS_PHOTOS -import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime -import org.mozilla.fenix.helpers.TestHelper.packageName /** * Implementation of Robot Pattern for download UI handling. @@ -52,14 +52,16 @@ class DownloadRobot { fun verifyDownloadedFileIcon() = assertDownloadedFileIcon() fun verifyEmptyDownloadsList() { - mDevice.findObject(UiSelector().resourceId("org.mozilla.fenix.debug:id/download_empty_view")) + mDevice.findObject(UiSelector().resourceId("$packageName:id/download_empty_view")) .waitForExists(waitingTime) onView(withText("No downloaded files")).check(matches(isDisplayed())) } fun waitForDownloadsListToExist() = - assertTrue(mDevice.findObject(UiSelector().resourceId("org.mozilla.fenix.debug:id/download_list")) - .waitForExists(waitingTime)) + assertTrue( + mDevice.findObject(UiSelector().resourceId("$packageName:id/download_list")) + .waitForExists(waitingTime) + ) class Transition { fun clickDownload(interact: DownloadRobot.() -> Unit): Transition { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/EnhancedTrackingProtectionRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/EnhancedTrackingProtectionRobot.kt index 5fb906ad7..e9dde4763 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/EnhancedTrackingProtectionRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/EnhancedTrackingProtectionRobot.kt @@ -179,8 +179,8 @@ private fun assertBasicLevelTrackingContentBlocked() { withText( containsString( "social-track-digest256.dummytracker.org\n" + - "ads-track-digest256.dummytracker.org\n" + - "analytics-track-digest256.dummytracker.org" + "ads-track-digest256.dummytracker.org\n" + + "analytics-track-digest256.dummytracker.org" ) ) ) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt index c7eb8dcec..430d6e310 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/HistoryRobot.kt @@ -94,7 +94,7 @@ class HistoryRobot { } fun openThreeDotMenu(interact: ThreeDotMenuHistoryItemRobot.() -> Unit): - ThreeDotMenuHistoryItemRobot.Transition { + ThreeDotMenuHistoryItemRobot.Transition { threeDotMenu().click() 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 2f62957d1..20abd2e87 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 @@ -13,15 +13,11 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.NoMatchingViewException import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.action.ViewActions.swipeLeft -import androidx.test.espresso.action.ViewActions.swipeRight -import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.Visibility import androidx.test.espresso.matcher.ViewMatchers.hasDescendant -import androidx.test.espresso.matcher.ViewMatchers.hasSibling import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withHint @@ -38,7 +34,6 @@ import androidx.test.uiautomator.Until import androidx.test.uiautomator.Until.findObject import junit.framework.TestCase.assertTrue import mozilla.components.browser.state.state.searchEngines -import mozilla.components.support.ktx.android.content.appName import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.containsString import org.hamcrest.CoreMatchers.instanceOf @@ -48,6 +43,8 @@ import org.junit.Assert import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime +import org.mozilla.fenix.helpers.TestHelper.appContext +import org.mozilla.fenix.helpers.TestHelper.appName import org.mozilla.fenix.helpers.TestHelper.packageName import org.mozilla.fenix.helpers.TestHelper.scrollToElementByText import org.mozilla.fenix.helpers.click @@ -63,10 +60,10 @@ import org.mozilla.fenix.ui.util.STRING_ONBOARDING_TRACKING_PROTECTION_HEADER */ class HomeScreenRobot { val privateSessionMessage = - "${appContext.appName} clears your search and browsing history from private tabs when you close them" + - " or quit the app. While this doesn’t make you anonymous to websites or your internet" + - " service provider, it makes it easier to keep what you do online private from anyone" + - " else who uses this device." + "$appName clears your search and browsing history from private tabs when you close them" + + " or quit the app. While this doesn’t make you anonymous to websites or your internet" + + " service provider, it makes it easier to keep what you do online private from anyone" + + " else who uses this device." fun verifyNavigationToolbar() = assertNavigationToolbar() fun verifyFocusedNavigationToolbar() = assertFocusedNavigationToolbar() @@ -119,127 +116,21 @@ class HomeScreenRobot { fun verifyExistingTopSitesTabs(title: String) = assertExistingTopSitesTabs(title) fun verifyTopSiteContextMenuItems() = assertTopSiteContextMenuItems() - // Collections element - fun clickCollectionThreeDotButton() { - collectionThreeDotButton().click() - mDevice.waitNotNull(findObject(text("Delete collection")), waitingTime) - } - - fun selectOpenTabs() { - onView(allOf(withText("Open tabs"))).click() - } - - fun selectRenameCollection() { - onView(allOf(withText("Rename collection"))).click() - mDevice.waitNotNull(findObject(text("Rename collection"))) - } - - fun selectAddTabToCollection() { - onView(allOf(withText("Add tab"))).click() - mDevice.waitNotNull(findObject(text("Select Tabs"))) - } - - fun selectDeleteCollection() { - onView(allOf(withText("Delete collection"))).click() - mDevice.waitNotNull(findObject(By.res("android:id/message")), waitingTime) - } - - fun confirmDeleteCollection() { - onView(allOf(withText("DELETE"))).click() - mDevice.waitNotNull( - findObject(By.res("$packageName:id/no_collections_header")), - waitingTime - ) - } - - fun verifyCollectionIsDisplayed(title: String) { - mDevice.findObject(UiSelector().text(title)).waitForExists(waitingTime) - collectionTitle(title).check(matches(isDisplayed())) - } - - fun verifyCollectionIcon() = onView(withId(R.id.collection_icon)).check(matches(isDisplayed())) - - fun expandCollection(title: String) { - try { - mDevice.waitNotNull(findObject(text(title)), waitingTime) - collectionTitle(title).click() - } catch (e: NoMatchingViewException) { - scrollToElementByText(title) - } - } - - fun collapseCollection(title: String) { - try { - mDevice.waitNotNull(findObject(text(title)), waitingTime) - onView(allOf(withId(R.id.chevron), hasSibling(withText(title)))).click() - } catch (e: NoMatchingViewException) { + // Collections elements + fun verifyCollectionIsDisplayed(title: String, collectionExists: Boolean = true) { + if (collectionExists) { scrollToElementByText(title) + assertTrue(mDevice.findObject(UiSelector().text(title)).waitForExists(waitingTime)) + } else { + scrollToElementByText("Collections") + assertTrue(mDevice.findObject(UiSelector().text(title)).waitUntilGone(waitingTime)) } } - fun verifyTabSavedInCollection(title: String, visible: Boolean = true) { - try { - collectionItem(title) - .check( - if (visible) matches(isDisplayed()) else doesNotExist() - ) - } catch (e: NoMatchingViewException) { - scrollToElementByText(title) - } - } - - fun verifyCollectionTabLogo() = - onView(withId(R.id.favicon)).check(matches(isDisplayed())) - - fun verifyCollectionTabUrl() = - onView(withId(R.id.caption)).check(matches(isDisplayed())) - - fun verifyShareCollectionButtonIsVisible(visible: Boolean) { - shareCollectionButton() - .check( - if (visible) matches(withEffectiveVisibility(Visibility.VISIBLE)) - else matches(withEffectiveVisibility(Visibility.GONE)) - ) - } - - fun verifyCollectionMenuIsVisible(visible: Boolean) { - collectionThreeDotButton() - .check( - if (visible) matches(withEffectiveVisibility(Visibility.VISIBLE)) - else matches(withEffectiveVisibility(Visibility.GONE)) - ) - } - - fun verifyCollectionItemRemoveButtonIsVisible(title: String, visible: Boolean) { - removeTabFromCollectionButton(title) - .check( - if (visible) matches(withEffectiveVisibility(Visibility.VISIBLE)) - else doesNotExist() - ) - } + fun verifyCollectionIcon() = onView(withId(R.id.collection_icon)).check(matches(isDisplayed())) fun verifyShareTabsOverlay() = assertShareTabsOverlay() - fun clickShareCollectionButton() = onView(withId(R.id.collection_share_button)).click() - - fun removeTabFromCollection(title: String) = removeTabFromCollectionButton(title).click() - - fun swipeCollectionItemRight(title: String) { - try { - collectionItem(title).perform(swipeRight()) - } catch (e: NoMatchingViewException) { - scrollToElementByText(title) - } - } - - fun swipeCollectionItemLeft(title: String) { - try { - collectionItem(title).perform(swipeLeft()) - } catch (e: NoMatchingViewException) { - scrollToElementByText(title) - } - } - fun togglePrivateBrowsingModeOnOff() { onView(ViewMatchers.withResourceName("privateBrowsingButton")) .perform(click()) @@ -316,7 +207,7 @@ class HomeScreenRobot { } fun triggerPrivateBrowsingShortcutPrompt(interact: AddToHomeScreenRobot.() -> Unit): AddToHomeScreenRobot.Transition { - // Loop to press the PB icon for 5 times to display the Add the Private Browsing Shortcut CFR + // Loop to press the PB icon for 5 times to display the Add the Private Browsing Shortcut CFR for (i in 1..5) { mDevice.findObject(UiSelector().resourceId("$packageName:id/privateBrowsingButton")) .waitForExists( @@ -415,6 +306,19 @@ class HomeScreenRobot { TabDrawerRobot().interact() return TabDrawerRobot.Transition() } + + fun expandCollection(title: String, interact: CollectionRobot.() -> Unit): CollectionRobot.Transition { + try { + mDevice.waitNotNull(findObject(text(title)), waitingTime) + collectionTitle(title).click() + } catch (e: NoMatchingViewException) { + scrollToElementByText(title) + collectionTitle(title).click() + } + + CollectionRobot().interact() + return CollectionRobot.Transition() + } } } @@ -424,7 +328,6 @@ fun homeScreen(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition } val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) -val appContext = InstrumentationRegistry.getInstrumentation().targetContext private fun homeScreenList() = UiScrollable( @@ -453,7 +356,7 @@ private fun assertFocusedNavigationToolbar() = private fun assertHomeScreen() { mDevice.findObject(UiSelector().resourceId("$packageName:id/homeLayout")).waitForExists(waitingTime) onView(ViewMatchers.withResourceName("homeLayout")) - .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) + .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) } private fun assertHomeMenu() = onView(ViewMatchers.withResourceName("menuButton")) @@ -480,7 +383,8 @@ private fun assertCollectionsHeader() = private fun assertNoCollectionsText() = onView( withText( - containsString("Collect the things that matter to you.\n" + + containsString( + "Collect the things that matter to you.\n" + "Group together similar searches, sites, and tabs for quick access later." ) ) @@ -510,7 +414,7 @@ private fun verifySearchEngineIcon(searchEngineName: String) { // First Run elements private fun assertWelcomeHeader() = - onView(allOf(withText("Welcome to ${appContext.appName}!"))) + onView(allOf(withText("Welcome to $appName!"))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertStartSyncHeader() { @@ -597,7 +501,7 @@ private fun assertYourPrivacyText() { onView( allOf( withText( - "We’ve designed ${appContext.appName} to give you control over what you share online and what you share with us." + "We’ve designed $appName to give you control over what you share online and what you share with us." ) ) ) @@ -637,9 +541,6 @@ private fun assertPrivateSessionMessage() = onView(withId(R.id.private_session_description)) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) -private fun collectionThreeDotButton() = - onView(allOf(withId(R.id.collection_overflow_button))) - private fun collectionTitle(title: String) = onView(allOf(withId(R.id.collection_title), withText(title))) @@ -678,21 +579,8 @@ private fun assertShareTabsOverlay() { private fun privateBrowsingButton() = onView(withId(R.id.privateBrowsingButton)) -private fun collectionItem(title: String) = - onView(allOf(withId(R.id.label), withText(title))) - private fun saveTabsToCollectionButton() = onView(withId(R.id.add_tabs_to_collections_button)) -private fun shareCollectionButton() = onView(withId(R.id.collection_share_button)) - -private fun removeTabFromCollectionButton(title: String) = - onView( - allOf( - withId(R.id.secondary_button), - hasSibling(withText(title)) - ) - ) - private fun tabsCounter() = onView(withId(R.id.tab_button)) private fun startBrowsingButton(): UiObject { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt index 10d39cca5..b38ec59eb 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/LibrarySubMenusMultipleSelectionToolbarRobot.kt @@ -58,7 +58,8 @@ class LibrarySubMenusMultipleSelectionToolbarRobot { mDevice.waitNotNull( Until.findObject( By.text("ALL ACTIONS") - ), waitingTime + ), + waitingTime ) } @@ -68,7 +69,8 @@ class LibrarySubMenusMultipleSelectionToolbarRobot { mDevice.waitNotNull( Until.findObject( By.text("ALL ACTIONS") - ), waitingTime + ), + waitingTime ) } 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 c6177bc46..d8ee7b7f1 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 @@ -11,7 +11,6 @@ import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.IdlingResource -import androidx.test.espresso.IdlingResourceTimeoutException import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.pressImeActionButton import androidx.test.espresso.action.ViewActions.replaceText @@ -33,6 +32,7 @@ import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.Until +import junit.framework.TestCase.assertTrue import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.anyOf import org.hamcrest.CoreMatchers.containsString @@ -70,8 +70,10 @@ class NavigationToolbarRobot { fun typeSearchTerm(searchTerm: String) = awesomeBar().perform(typeText(searchTerm)) fun toggleReaderView() { - mDevice.findObject(UiSelector() - .resourceId("$packageName:id/mozac_browser_toolbar_page_actions")) + mDevice.findObject( + UiSelector() + .resourceId("$packageName:id/mozac_browser_toolbar_page_actions") + ) .waitForExists(waitingTime) readerViewToggle().click() @@ -116,43 +118,41 @@ class NavigationToolbarRobot { return BrowserRobot.Transition() } - fun openTrackingProtectionTestPage(url: Uri, etpEnabled: Boolean, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { - sessionLoadedIdlingResource = SessionLoadedIdlingResource() - + fun openTrackingProtectionTestPage( + url: Uri, + etpEnabled: Boolean, + interact: BrowserRobot.() -> Unit + ): BrowserRobot.Transition { openEditURLView() awesomeBar().perform(replaceText(url.toString()), pressImeActionButton()) - runWithIdleRes(sessionLoadedIdlingResource) { - when (etpEnabled) { - true -> - try { - onView(withId(R.id.onboarding_message)) - .check(matches(isDisplayed())) - } catch (e: IdlingResourceTimeoutException) { - openThreeDotMenu { - }.stopPageLoad { - val onboardingDisplayed = - mDevice.findObject(UiSelector().resourceId("$packageName:id/onboarding_message")) - .waitForExists(waitingTime) - - if (!onboardingDisplayed) { - openThreeDotMenu { - }.refreshPage {} + val onboardingMessage = + mDevice.findObject(UiSelector().resourceId("$packageName:id/onboarding_message")) + + val onboardingDisplayed = onboardingMessage.waitForExists(waitingTime) + + when (etpEnabled) { + true -> + try { + assertTrue( + "Onboarding message not displayed", + onboardingDisplayed + ) + } catch (e: AssertionError) { + openThreeDotMenu { + }.stopPageLoad { + if (!onboardingDisplayed) { + openThreeDotMenu { + }.refreshPage { + assertTrue(onboardingDisplayed) } } } + } - false -> - try { - onView(withResourceName("browserLayout")).check(matches(isDisplayed())) - } catch (e: IdlingResourceTimeoutException) { - openThreeDotMenu { - }.stopPageLoad { - }.openThreeDotMenu { - }.refreshPage {} - } - } + false -> + onView(withResourceName("browserLayout")).check(matches(isDisplayed())) } BrowserRobot().interact() @@ -187,8 +187,10 @@ class NavigationToolbarRobot { fun openTabTray(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition { mDevice.waitForIdle(waitingTime) tabTrayButton().click() - mDevice.waitNotNull(Until.findObject(By.res("$packageName:id/tab_layout")), - waitingTime) + mDevice.waitNotNull( + Until.findObject(By.res("$packageName:id/tab_layout")), + waitingTime + ) TabDrawerRobot().interact() return TabDrawerRobot.Transition() @@ -254,7 +256,8 @@ class NavigationToolbarRobot { RecyclerViewActions.actionOnItem( hasDescendant( withText("Close tab") - ), ViewActions.click() + ), + ViewActions.click() ) ) @@ -270,7 +273,8 @@ class NavigationToolbarRobot { RecyclerViewActions.actionOnItem( hasDescendant( withText("New tab") - ), ViewActions.click() + ), + ViewActions.click() ) ) @@ -286,7 +290,8 @@ class NavigationToolbarRobot { RecyclerViewActions.actionOnItem( hasDescendant( withText("New private tab") - ), ViewActions.click() + ), + ViewActions.click() ) ) @@ -355,8 +360,10 @@ private fun readerViewToggle() = onView(withParent(withId(R.id.mozac_browser_toolbar_page_actions))) private fun assertReaderViewDetected(visible: Boolean) { - mDevice.findObject(UiSelector() - .description("Reader view")) + mDevice.findObject( + UiSelector() + .description("Reader view") + ) .waitForExists(waitingTime) onView( @@ -371,8 +378,10 @@ private fun assertReaderViewDetected(visible: Boolean) { } private fun assertCloseReaderViewDetected(visible: Boolean) { - mDevice.findObject(UiSelector() - .description("Close reader view")) + mDevice.findObject( + UiSelector() + .description("Close reader view") + ) .waitForExists(waitingTime) onView( diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ReaderViewRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ReaderViewRobot.kt index 8532d5af3..2ebaafc8b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ReaderViewRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ReaderViewRobot.kt @@ -55,9 +55,9 @@ class ReaderViewRobot { val prefs = InstrumentationRegistry.getInstrumentation() .targetContext.getSharedPreferences( - "mozac_feature_reader_view", - Context.MODE_PRIVATE - ) + "mozac_feature_reader_view", + Context.MODE_PRIVATE + ) assertEquals(fontType, prefs.getString(fontTypeKey, "")) } @@ -67,9 +67,9 @@ class ReaderViewRobot { val prefs = InstrumentationRegistry.getInstrumentation() .targetContext.getSharedPreferences( - "mozac_feature_reader_view", - Context.MODE_PRIVATE - ) + "mozac_feature_reader_view", + Context.MODE_PRIVATE + ) val fontSizeKeyValue = prefs.getInt(fontSizeKey, 3) @@ -81,9 +81,9 @@ class ReaderViewRobot { val prefs = InstrumentationRegistry.getInstrumentation() .targetContext.getSharedPreferences( - "mozac_feature_reader_view", - Context.MODE_PRIVATE - ) + "mozac_feature_reader_view", + Context.MODE_PRIVATE + ) assertEquals(expectedColorScheme, prefs.getString(colorSchemeKey, "")) } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/RecentlyClosedTabsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/RecentlyClosedTabsRobot.kt index c2bbd5cad..b112db7e2 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/RecentlyClosedTabsRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/RecentlyClosedTabsRobot.kt @@ -18,6 +18,7 @@ import org.hamcrest.Matchers import org.hamcrest.Matchers.allOf import org.mozilla.fenix.R import org.mozilla.fenix.helpers.TestAssetHelper +import org.mozilla.fenix.helpers.TestHelper.packageName import org.mozilla.fenix.helpers.click /** @@ -27,7 +28,7 @@ import org.mozilla.fenix.helpers.click class RecentlyClosedTabsRobot { fun waitForListToExist() = - mDevice.findObject(UiSelector().resourceId("org.mozilla.fenix.debug:id/recently_closed_list")) + mDevice.findObject(UiSelector().resourceId("$packageName:id/recently_closed_list")) .waitForExists( TestAssetHelper.waitingTime ) @@ -93,7 +94,8 @@ private fun assertRecentlyClosedTabsMenuView() { ) ) .check( - matches(withEffectiveVisibility(Visibility.VISIBLE))) + matches(withEffectiveVisibility(Visibility.VISIBLE)) + ) } private fun assertEmptyRecentlyClosedTabsList() = @@ -104,7 +106,8 @@ private fun assertEmptyRecentlyClosedTabsList() = ) ) .check( - matches(withText("No recently closed tabs here"))) + matches(withText("No recently closed tabs here")) + ) private fun assertPageUrl(expectedUrl: Uri) = onView( allOf( @@ -115,7 +118,8 @@ private fun assertPageUrl(expectedUrl: Uri) = onView( ) ) .check( - matches(withText(Matchers.containsString(expectedUrl.toString())))) + matches(withText(Matchers.containsString(expectedUrl.toString()))) + ) private fun recentlyClosedTabsPageTitle() = onView( allOf( @@ -127,9 +131,11 @@ private fun recentlyClosedTabsPageTitle() = onView( private fun assertRecentlyClosedTabsPageTitle(title: String) { recentlyClosedTabsPageTitle() .check( - matches(withEffectiveVisibility(Visibility.VISIBLE))) + matches(withEffectiveVisibility(Visibility.VISIBLE)) + ) .check( - matches(withText(title))) + matches(withText(title)) + ) } private fun recentlyClosedTabsThreeDotButton() = @@ -137,45 +143,50 @@ private fun recentlyClosedTabsThreeDotButton() = allOf( withId(R.id.overflow_menu), withEffectiveVisibility( - Visibility.VISIBLE + Visibility.VISIBLE + ) ) ) -) private fun assertRecentlyClosedTabsMenuCopy() = onView(withText("Copy")) .check( matches( - withEffectiveVisibility(Visibility.VISIBLE))) + withEffectiveVisibility(Visibility.VISIBLE) + ) + ) private fun assertRecentlyClosedTabsMenuShare() = onView(withText("Share")) .check( matches( - withEffectiveVisibility(Visibility.VISIBLE))) + withEffectiveVisibility(Visibility.VISIBLE) + ) + ) private fun assertRecentlyClosedTabsOverlayNewTab() = onView(withText("Open in new tab")) .check( matches( - withEffectiveVisibility(Visibility.VISIBLE)) -) + withEffectiveVisibility(Visibility.VISIBLE) + ) + ) private fun assertRecentlyClosedTabsMenuPrivateTab() = onView(withText("Open in private tab")) .check( matches( withEffectiveVisibility(Visibility.VISIBLE) + ) ) - ) private fun assertRecentlyClosedTabsMenuDelete() = onView(withText("Delete")) .check( matches( withEffectiveVisibility(Visibility.VISIBLE) - ) -) + ) + ) private fun recentlyClosedTabsCopyButton() = onView(withText("Copy")) @@ -184,26 +195,31 @@ private fun copySnackBarText() = onView(withId(R.id.snackbar_text)) private fun assertCopySnackBarText() = copySnackBarText() .check( matches - (withText("URL copied"))) + (withText("URL copied")) + ) private fun recentlyClosedTabsShareButton() = onView(withText("Share")) private fun assertRecentlyClosedShareOverlay() = onView(withId(R.id.shareWrapper)) .check( - matches(ViewMatchers.isDisplayed())) + matches(ViewMatchers.isDisplayed()) + ) private fun assetRecentlyClosedShareTitle(title: String) = onView(withId(R.id.share_tab_title)) .check( - matches(ViewMatchers.isDisplayed())) + matches(ViewMatchers.isDisplayed()) + ) .check( - matches(withText(title))) + matches(withText(title)) + ) private fun assertRecentlyClosedShareFavicon() = onView(withId(R.id.share_tab_favicon)) .check( - matches(ViewMatchers.isDisplayed())) + matches(ViewMatchers.isDisplayed()) + ) private fun assertRecentlyClosedShareUrl(expectedUrl: Uri) = onView( @@ -213,7 +229,8 @@ private fun assertRecentlyClosedShareUrl(expectedUrl: Uri) = ) ) .check( - matches(withText(Matchers.containsString(expectedUrl.toString())))) + matches(withText(Matchers.containsString(expectedUrl.toString()))) + ) private fun recentlyClosedTabsNewTabButton() = onView(withText("Open in new tab")) 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 a9ccd138a..3048f1876 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 @@ -22,6 +22,7 @@ import androidx.test.espresso.matcher.ViewMatchers.hasDescendant 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.withSubstring import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By @@ -35,6 +36,7 @@ import org.hamcrest.CoreMatchers.startsWith import org.hamcrest.Matchers import org.junit.Assert.assertEquals import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.Constants.LONG_CLICK_DURATION import org.mozilla.fenix.helpers.SessionLoadedIdlingResource import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime @@ -71,8 +73,10 @@ class SearchRobot { selectDefaultSearchEngine(searchEngineName) fun clickSearchEngineShortcutButton() { - val searchEnginesShortcutButton = mDevice.findObject(UiSelector() - .resourceId("$packageName:id/search_engines_shortcut_button")) + val searchEnginesShortcutButton = mDevice.findObject( + UiSelector() + .resourceId("$packageName:id/search_engines_shortcut_button") + ) searchEnginesShortcutButton.waitForExists(waitingTime) searchEnginesShortcutButton.click() } @@ -126,6 +130,24 @@ class SearchRobot { clearButton().perform(click()) } + fun longClickToolbar() { + mDevice.waitForWindowUpdate(packageName, waitingTime) + mDevice.findObject(UiSelector().resourceId("$packageName:id/awesomeBar")) + .waitForExists(waitingTime) + mDevice.findObject(UiSelector().resourceId("$packageName:id/toolbar")) + .waitForExists(waitingTime) + val toolbar = mDevice.findObject(By.res("$packageName:id/toolbar")) + toolbar.click(LONG_CLICK_DURATION) + } + + fun clickPasteText() { + mDevice.findObject(UiSelector().textContains("Paste")).waitForExists(waitingTime) + val pasteText = mDevice.findObject(By.textContains("Paste")) + pasteText.click() + } + + fun verifyPastedToolbarText(expectedText: String) = assertPastedToolbarText(expectedText) + class Transition { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) private lateinit var sessionLoadedIdlingResource: SessionLoadedIdlingResource @@ -202,10 +224,10 @@ 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")), + Until.findObject(By.textContains("${searchEngineName.lowercase()}.com/?q=mozilla")), TestAssetHelper.waitingTime ) - onView(allOf(withText(startsWith("${searchEngineName.toLowerCase()}.com")))) + onView(allOf(withText(startsWith("${searchEngineName.lowercase()}.com")))) .check(matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) } @@ -250,7 +272,8 @@ private fun assertKeyboardVisibility(isExpectedToBeVisible: Boolean) = { mDevice.waitNotNull( Until.findObject( By.text("Search Engine") - ), waitingTime + ), + waitingTime ) assertEquals( isExpectedToBeVisible, @@ -295,4 +318,17 @@ private fun assertDefaultSearchEngine(expectedText: String) { .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) } +private fun assertPastedToolbarText(expectedText: String) { + mDevice.findObject(UiSelector().resourceId("$packageName:id/toolbar")) + .waitForExists(waitingTime) + mDevice.findObject(UiSelector().resourceId("$packageName:id/mozac_browser_toolbar_url_view")) + .waitForExists(waitingTime) + onView( + allOf( + withSubstring(expectedText), + withId(R.id.mozac_browser_toolbar_edit_url_view) + ) + ).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) +} + private fun goBackButton() = onView(allOf(withContentDescription("Navigate up"))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt index c0d8f33bf..411795c4a 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsRobot.kt @@ -33,6 +33,7 @@ import org.hamcrest.CoreMatchers import org.mozilla.fenix.R import org.mozilla.fenix.helpers.Constants.PackageName.GOOGLE_PLAY_SERVICES import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime +import org.mozilla.fenix.helpers.TestHelper.appName import org.mozilla.fenix.helpers.TestHelper.scrollToElementByText import org.mozilla.fenix.helpers.assertIsEnabled import org.mozilla.fenix.helpers.click @@ -110,7 +111,7 @@ class SettingsRobot { } fun openAboutFirefoxPreview(interact: SettingsSubMenuAboutRobot.() -> Unit): - SettingsSubMenuAboutRobot.Transition { + SettingsSubMenuAboutRobot.Transition { assertAboutFirefoxPreview().click() @@ -119,7 +120,7 @@ class SettingsRobot { } fun openSearchSubMenu(interact: SettingsSubMenuSearchRobot.() -> Unit): - SettingsSubMenuSearchRobot.Transition { + SettingsSubMenuSearchRobot.Transition { fun searchEngineButton() = onView(withText("Search")) searchEngineButton().click() @@ -147,15 +148,19 @@ class SettingsRobot { } fun openAccessibilitySubMenu(interact: SettingsSubMenuAccessibilityRobot.() -> Unit): SettingsSubMenuAccessibilityRobot.Transition { + scrollToElementByText("Accessibility") fun accessibilityButton() = onView(withText("Accessibility")) - accessibilityButton().click() + accessibilityButton() + .check(matches(isDisplayed())) + .click() SettingsSubMenuAccessibilityRobot().interact() return SettingsSubMenuAccessibilityRobot.Transition() } fun openLanguageSubMenu(interact: SettingsSubMenuLanguageRobot.() -> Unit): SettingsSubMenuLanguageRobot.Transition { + scrollToElementByText("Language") fun languageButton() = onView(withText("Language")) languageButton().click() @@ -310,14 +315,8 @@ private fun assertDefaultBrowserIsDisabled() { } private fun toggleDefaultBrowserSwitch() { - scrollToElementByText("Set as default browser") - onView( - CoreMatchers.allOf( - ViewMatchers.withParent(CoreMatchers.not(withId(R.id.navigationToolbar))), - withText("Set as default browser") - ) - ) - .perform(ViewActions.click()) + scrollToElementByText("Privacy and security") + onView(withText("Set as default browser")).perform(ViewActions.click()) } private fun assertAndroidDefaultAppsMenuAppears() { @@ -402,7 +401,7 @@ private fun assertNotificationsButton() { private fun assertDataCollectionButton() { scrollToElementByText("Data collection") onView(withText("Data collection")) - .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) + .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) } private fun openLinksInAppsButton() = onView(withText("Open links in apps")) @@ -475,8 +474,8 @@ private fun assertRateOnGooglePlay(): ViewInteraction { private fun assertAboutFirefoxPreview(): ViewInteraction { onView(withId(R.id.recycler_view)) - .perform(RecyclerViewActions.scrollTo(hasDescendant(withText("About Firefox Preview")))) - return onView(withText("About Firefox Preview")) + .perform(RecyclerViewActions.scrollTo(hasDescendant(withText("About $appName")))) + return onView(withText("About $appName")) .check(matches(isDisplayed())) } 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 fe6986fe4..0c651c857 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 @@ -28,6 +28,7 @@ import org.hamcrest.CoreMatchers.containsString import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.R import org.mozilla.fenix.helpers.TestHelper +import org.mozilla.fenix.helpers.TestHelper.appName import org.mozilla.fenix.helpers.isVisibleForUser import org.mozilla.fenix.settings.SupportUtils import java.text.SimpleDateFormat @@ -108,7 +109,7 @@ private fun assertVersionNumber() { private fun assertProductCompany() { onView(withId(R.id.about_content)) - .check(matches(withText(containsString("Firefox Preview is produced by Mozilla.")))) + .check(matches(withText(containsString("$appName is produced by Mozilla.")))) } private fun assertCurrentTimestamp() { @@ -116,17 +117,17 @@ private fun assertCurrentTimestamp() { // Currently UI tests run against debug builds, which display a hard-coded string 'debug build' // instead of the date. See https://github.com/mozilla-mobile/fenix/pull/10812#issuecomment-633746833 .check(matches(withText(containsString("debug build")))) - // This assertion should be valid for non-debug build types. - // .check(BuildDateAssertion.isDisplayedDateAccurate()) + // This assertion should be valid for non-debug build types. + // .check(BuildDateAssertion.isDisplayedDateAccurate()) } private fun assertWhatIsNewInFirefoxPreview() { - if (!onView(withText("What’s new in Firefox Preview")).isVisibleForUser()) { + if (!onView(withText("What’s new in $appName")).isVisibleForUser()) { onView(withId(R.id.about_layout)).perform(ViewActions.swipeUp()) } - onView(withText("What’s new in Firefox Preview")) + onView(withText("What’s new in $appName")) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .perform(click()) @@ -222,7 +223,7 @@ private fun assertLibrariesUsed() { .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .perform(click()) - onView(withId(R.id.navigationToolbar)).check(matches(hasDescendant(withText(containsString("Firefox Preview | OSS Libraries"))))) + onView(withId(R.id.navigationToolbar)).check(matches(hasDescendant(withText(containsString("$appName | OSS Libraries"))))) Espresso.pressBack() } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAddonsManagerRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAddonsManagerRobot.kt index 102367770..fae51e4fc 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAddonsManagerRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAddonsManagerRobot.kt @@ -2,31 +2,35 @@ package org.mozilla.fenix.ui.robots import android.widget.RelativeLayout import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.IdlingResourceTimeoutException import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.Visibility import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.hasSibling import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA import androidx.test.espresso.matcher.ViewMatchers.withContentDescription -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility -import androidx.test.espresso.matcher.ViewMatchers.Visibility +import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withParent +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.rule.ActivityTestRule import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.Until import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.containsString import org.hamcrest.CoreMatchers.instanceOf import org.hamcrest.CoreMatchers.not +import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R -import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.helpers.IdlingResourceHelper.registerAddonInstallingIdlingResource import org.mozilla.fenix.helpers.IdlingResourceHelper.unregisterAddonInstallingIdlingResource import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime import org.mozilla.fenix.helpers.TestHelper +import org.mozilla.fenix.helpers.TestHelper.appName import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.ext.waitNotNull @@ -36,9 +40,23 @@ import org.mozilla.fenix.helpers.ext.waitNotNull class SettingsSubMenuAddonsManagerRobot { fun verifyAddonPrompt(addonName: String) = assertAddonPrompt(addonName) + fun clickInstallAddon(addonName: String) = selectInstallAddon(addonName) - fun verifyDownloadAddonPrompt(addonName: String, activityTestRule: HomeActivityTestRule) = - assertDownloadingAddonPrompt(addonName, activityTestRule) + + fun verifyDownloadAddonPrompt( + addonName: String, + activityTestRule: ActivityTestRule + ) { + try { + assertDownloadingAddonPrompt(addonName, activityTestRule) + } catch (e: IdlingResourceTimeoutException) { + if (mDevice.findObject(UiSelector().text("Failed to install $addonName")).exists()) { + clickInstallAddon(addonName) + acceptInstallAddon() + assertDownloadingAddonPrompt(addonName, activityTestRule) + } + } + } fun cancelInstallAddon() = cancelInstall() fun acceptInstallAddon() = allowInstall() @@ -124,7 +142,7 @@ class SettingsSubMenuAddonsManagerRobot { private fun assertDownloadingAddonPrompt( addonName: String, - activityTestRule: HomeActivityTestRule + activityTestRule: ActivityTestRule ) { registerAddonInstallingIdlingResource(activityTestRule) @@ -132,7 +150,7 @@ class SettingsSubMenuAddonsManagerRobot { allOf( withText("Okay, Got it"), withParent(instanceOf(RelativeLayout::class.java)), - hasSibling(withText("$addonName has been added to Firefox Preview")), + hasSibling(withText("$addonName has been added to $appName")), hasSibling(withText("Open it in the menu")), hasSibling(withText("Allow in private browsing")) ) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDataCollectionRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDataCollectionRobot.kt index c4dbca480..9e561038a 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDataCollectionRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDataCollectionRobot.kt @@ -16,6 +16,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import org.hamcrest.CoreMatchers.allOf import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.TestHelper.appName import org.mozilla.fenix.helpers.assertIsEnabled import org.mozilla.fenix.helpers.click @@ -58,8 +59,11 @@ private fun goBackButton() = onView(withContentDescription("Navigate up")) private fun assertNavigationToolBarHeader() = onView( - allOf(withParent(withId(R.id.navigationToolbar)), - withText(R.string.preferences_data_collection))) + allOf( + withParent(withId(R.id.navigationToolbar)), + withText(R.string.preferences_data_collection) + ) +) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertDataCollectionOptions() { @@ -68,7 +72,7 @@ private fun assertDataCollectionOptions() { .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) val usageAndTechnicalDataText = - "Shares performance, usage, hardware and customization data about your browser with Mozilla to help us make Firefox Preview better" + "Shares performance, usage, hardware and customization data about your browser with Mozilla to help us make $appName better" onView(withText(usageAndTechnicalDataText)) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDeleteBrowsingDataOnQuitRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDeleteBrowsingDataOnQuitRobot.kt index 8d0395e5b..ffc8060a6 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDeleteBrowsingDataOnQuitRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDeleteBrowsingDataOnQuitRobot.kt @@ -62,18 +62,27 @@ class SettingsSubMenuDeleteBrowsingDataOnQuitRobot { private fun goBackButton() = onView(withContentDescription("Navigate up")) -private fun assertNavigationToolBarHeader() = onView(allOf(withId(R.id.navigationToolbar), - withChild(withText(R.string.preferences_delete_browsing_data_on_quit)))) +private fun assertNavigationToolBarHeader() = onView( + allOf( + withId(R.id.navigationToolbar), + withChild(withText(R.string.preferences_delete_browsing_data_on_quit)) + ) +) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) -private fun deleteBrowsingOnQuitButton() = onView(allOf(withParentIndex(0), - withChild(withText(R.string.preferences_delete_browsing_data_on_quit)))) +private fun deleteBrowsingOnQuitButton() = onView( + allOf( + withParentIndex(0), + withChild(withText(R.string.preferences_delete_browsing_data_on_quit)) + ) +) private fun assertDeleteBrowsingOnQuitButton() = deleteBrowsingOnQuitButton() .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertDeleteBrowsingOnQuitButtonSummary() = onView( - withText(R.string.preference_summary_delete_browsing_data_on_quit_2)) + withText(R.string.preference_summary_delete_browsing_data_on_quit_2) +) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) private fun assertDeleteBrowsingOnQuitButtonSwitchDefault() = onView(withResourceName("switch_widget")) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDeleteBrowsingDataRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDeleteBrowsingDataRobot.kt index 32ab7c4f2..53a86f37e 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDeleteBrowsingDataRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuDeleteBrowsingDataRobot.kt @@ -21,9 +21,10 @@ import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.Until import org.hamcrest.CoreMatchers.allOf import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.TestAssetHelper +import org.mozilla.fenix.helpers.TestHelper.appName import org.mozilla.fenix.helpers.assertIsChecked import org.mozilla.fenix.helpers.click -import org.mozilla.fenix.helpers.TestAssetHelper /** * Implementation of Robot Pattern for the settings Delete Browsing Data sub menu. @@ -85,8 +86,12 @@ private fun goBackButton() = onView(allOf(withContentDescription("Navigate up"))) private fun assertNavigationToolBarHeader() { - onView(allOf(withId(R.id.navigationToolbar), - withChild(withText(R.string.preferences_delete_browsing_data)))) + onView( + allOf( + withId(R.id.navigationToolbar), + withChild(withText(R.string.preferences_delete_browsing_data)) + ) + ) .check((matches(withEffectiveVisibility(Visibility.VISIBLE)))) } @@ -104,7 +109,7 @@ private fun cancelButton() = mDevice.findObject(UiSelector().textStartsWith("CANCEL")) private fun assertMessageInDialogBox() = - onView(withText("Firefox Preview will delete the selected browsing data.")) + onView(withText("$appName will delete the selected browsing data.")) .inRoot(isDialog()) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuEnhancedTrackingProtectionRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuEnhancedTrackingProtectionRobot.kt index e3fe7d6fa..c60f8e965 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuEnhancedTrackingProtectionRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuEnhancedTrackingProtectionRobot.kt @@ -10,9 +10,9 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.pressBack import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions +import androidx.test.espresso.matcher.ViewMatchers.Visibility import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.hasSibling -import androidx.test.espresso.matcher.ViewMatchers.Visibility import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withChild import androidx.test.espresso.matcher.ViewMatchers.withContentDescription @@ -26,6 +26,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.not +import org.mozilla.fenix.helpers.TestHelper.appName import org.mozilla.fenix.helpers.TestHelper.scrollToElementByText import org.mozilla.fenix.helpers.assertIsChecked import org.mozilla.fenix.helpers.click @@ -110,8 +111,12 @@ class SettingsSubMenuEnhancedTrackingProtectionRobot { } private fun assertNavigationToolBarHeader() { - onView(allOf(withParent(withId(org.mozilla.fenix.R.id.navigationToolbar)), - withText("Enhanced Tracking Protection"))) + onView( + allOf( + withParent(withId(org.mozilla.fenix.R.id.navigationToolbar)), + withText("Enhanced Tracking Protection") + ) + ) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) } @@ -121,21 +126,32 @@ private fun assertEnhancedTrackingProtectionHeader() { } private fun assertEnhancedTrackingProtectionHeaderDescription() { - onView(allOf(withParent(withParentIndex(0)), - withText("Keep your data to yourself. Firefox Preview protects you from many of the most common trackers that follow what you do online."))) + onView( + allOf( + withParent(withParentIndex(0)), + withText("Keep your data to yourself. $appName protects you from many of the most common trackers that follow what you do online.") + ) + ) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) } private fun assertLearnMoreText() { - onView(allOf(withParent(withParentIndex(0)), - withText("Learn more"))) + onView( + allOf( + withParent(withParentIndex(0)), + withText("Learn more") + ) + ) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) } private fun assertEnhancedTrackingProtectionTextWithSwitchWidget() { - onView(allOf( + onView( + allOf( withParentIndex(1), - withChild(withText("Enhanced Tracking Protection")))) + withChild(withText("Enhanced Tracking Protection")) + ) + ) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) } @@ -194,7 +210,8 @@ private fun assertTrackingProtectionSwitchEnabled() { } private fun assertRadioButtonDefaults() { - onView(withText("Strict") + onView( + withText("Strict") ).assertIsChecked(false) onView( @@ -204,7 +221,8 @@ private fun assertRadioButtonDefaults() { ) ).assertIsChecked(true) - onView(withText("Custom") + onView( + withText("Custom") ).assertIsChecked(false) } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLanguageRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLanguageRobot.kt index 1dd28b2a7..ed7a98dcf 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLanguageRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLanguageRobot.kt @@ -9,9 +9,24 @@ import androidx.test.espresso.action.ViewActions import androidx.test.espresso.matcher.ViewMatchers import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.UiScrollable +import androidx.test.uiautomator.UiSelector +import junit.framework.TestCase.assertTrue import org.hamcrest.CoreMatchers +import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime +import org.mozilla.fenix.helpers.TestHelper.packageName class SettingsSubMenuLanguageRobot { + fun selectLanguage(language: String) { + languagesList.waitForExists(waitingTime) + languagesList + .getChildByText(UiSelector().text(language), language) + .click() + } + + fun verifyLanguageHeaderIsTranslated(translation: String) = + assertTrue(mDevice.findObject(UiSelector().text(translation)).waitForExists(waitingTime)) + class Transition { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) @@ -27,3 +42,10 @@ class SettingsSubMenuLanguageRobot { private fun goBackButton() = onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up"))) + +private val languagesList = + UiScrollable( + UiSelector() + .resourceId("$packageName:id/locale_list") + .scrollable(true) + ) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot.kt index b7e3199db..581782744 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot.kt @@ -18,10 +18,10 @@ import org.hamcrest.CoreMatchers class SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot { fun verifySaveLoginsOptionsView() { onView(ViewMatchers.withText("Ask to save")) - .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) onView(ViewMatchers.withText("Never save")) - .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) } class Transition { @@ -35,4 +35,4 @@ class SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot { } private fun goBackButton() = - onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up"))) + onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up"))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLoginsAndPasswordRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLoginsAndPasswordRobot.kt index 74a6805de..c0a513fcb 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLoginsAndPasswordRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuLoginsAndPasswordRobot.kt @@ -6,6 +6,7 @@ package org.mozilla.fenix.ui.robots +import android.content.Context import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions import androidx.test.espresso.assertion.ViewAssertions.matches @@ -16,6 +17,7 @@ import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until import org.hamcrest.CoreMatchers +import org.mozilla.fenix.R import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.ext.waitNotNull @@ -40,7 +42,7 @@ class SettingsSubMenuLoginsAndPasswordRobot { fun verifyDefaultValueExceptions() = assertDefaultValueExceptions() - fun verifyDefaultValueAutofillLogins() = assertDefaultValueAutofillLogins() + fun verifyDefaultValueAutofillLogins(context: Context) = assertDefaultValueAutofillLogins(context) class Transition { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) @@ -92,16 +94,23 @@ fun settingsSubMenuLoginsAndPassword(interact: SettingsSubMenuLoginsAndPasswordR } private fun goBackButton() = - onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up"))) + onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up"))) private fun assertDefaultView() = onView(ViewMatchers.withText("Sync logins across devices")) - .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) -private fun assertDefaultValueAutofillLogins() = onView(ViewMatchers.withText("Autofill")) +private fun assertDefaultValueAutofillLogins(context: Context) = onView( + ViewMatchers.withText( + context.getString( + R.string.preferences_passwords_autofill2, + context.getString(R.string.app_name) + ) + ) +) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) private fun assertDefaultValueExceptions() = onView(ViewMatchers.withText("Exceptions")) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) private fun assertDefaultValueSyncLogins() = onView(ViewMatchers.withText("Sign in to Sync")) - .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuPrivateBrowsingRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuPrivateBrowsingRobot.kt index c8ef29dd0..4a5a23438 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuPrivateBrowsingRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuPrivateBrowsingRobot.kt @@ -25,6 +25,7 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.mozilla.fenix.R import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime +import org.mozilla.fenix.helpers.TestHelper.appName import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.isEnabled @@ -104,7 +105,7 @@ private fun goBackButton() = onView(withContentDescription("Navigate up")) private fun addAutomaticallyButton() = mDevice.findObject(UiSelector().textStartsWith("add automatically")) -private fun privateBrowsingShortcutIcon() = mDevice.findObject(text("Private Firefox Preview")) +private fun privateBrowsingShortcutIcon() = mDevice.findObject(text("Private $appName")) private fun assertAddPrivateBrowsingShortcutButton() { mDevice.wait( @@ -130,6 +131,6 @@ private fun assertOpenLinksInPrivateTabOff() { } private fun assertPrivateBrowsingShortcutIcon() { - mDevice.wait(Until.findObject(text("Private Firefox Preview")), waitingTime) - assertTrue(mDevice.hasObject(text("Private Firefox Preview"))) + mDevice.wait(Until.findObject(text("Private $appName")), waitingTime) + assertTrue(mDevice.hasObject(text("Private $appName"))) } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSearchRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSearchRobot.kt index 92c29ce00..435ce4a8f 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSearchRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSearchRobot.kt @@ -24,6 +24,7 @@ import androidx.test.uiautomator.UiSelector import org.hamcrest.CoreMatchers import org.mozilla.fenix.R import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime +import org.mozilla.fenix.helpers.TestHelper.packageName import org.mozilla.fenix.helpers.click /** @@ -53,7 +54,7 @@ class SettingsSubMenuSearchRobot { fun saveNewSearchEngine() { addSearchEngineSaveButton().click() mDevice.findObject( - UiSelector().resourceId("org.mozilla.fenix.debug:id/recycler_view") + UiSelector().resourceId("$packageName:id/recycler_view") ).waitForExists(waitingTime) } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSitePermissionsCommonRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSitePermissionsCommonRobot.kt index 566508837..e06dc670b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSitePermissionsCommonRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSitePermissionsCommonRobot.kt @@ -139,11 +139,11 @@ private fun assertCheckAutoPayRadioButtonDefault() { // Block audio only onView(withId(R.id.third_radio)) - .assertIsChecked(isChecked = false) + .assertIsChecked(isChecked = true) // Block audio and video onView(withId(R.id.fourth_radio)) - .assertIsChecked(isChecked = true) + .assertIsChecked(isChecked = false) } private fun assertAskToAllowRecommended() = onView(withId(R.id.ask_to_allow_radio)) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSitePermissionsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSitePermissionsRobot.kt index 4c5a96629..ba0a4bbb7 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSitePermissionsRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuSitePermissionsRobot.kt @@ -159,7 +159,7 @@ class SettingsSubMenuSitePermissionsRobot { onView(withText("Autoplay")) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) - val autoplayText = "Block audio and video" + val autoplayText = "Block audio only" onView(withText(autoplayText)) .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt index d1fbaf01b..3cf81bf25 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuTabsRobot.kt @@ -9,11 +9,14 @@ package org.mozilla.fenix.ui.robots import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import org.hamcrest.CoreMatchers.allOf +import org.mozilla.fenix.helpers.click /** * Implementation of Robot Pattern for the settings Tabs sub menu. @@ -22,6 +25,10 @@ class SettingsSubMenuTabsRobot { fun verifyOptions() = assertOptions() + fun verifyStartOnHomeOptions() = assertStartOnHomeOptions() + + fun clickAlwaysStartOnHomeToggle() = alwaysStartOnHomeToggle().click() + class Transition { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) @@ -46,6 +53,17 @@ private fun assertOptions() { .check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) } +private fun assertStartOnHomeOptions() { + startOnHomeHeading() + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + afterFourHoursToggle() + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + alwaysStartOnHomeToggle() + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + neverStartOnHomeToggle() + .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) +} + private fun manualToggle() = onView(withText("Manually")) private fun afterOneDayToggle() = onView(withText("After one day")) @@ -54,5 +72,13 @@ private fun afterOneWeekToggle() = onView(withText("After one week")) private fun afterOneMonthToggle() = onView(withText("After one month")) +private fun startOnHomeHeading() = onView(withText("Start on home")) + +private fun afterFourHoursToggle() = onView(withText("After four hours")) + +private fun alwaysStartOnHomeToggle() = onView(withText("Always")) + +private fun neverStartOnHomeToggle() = onView(withText("Never")) + private fun goBackButton() = onView(allOf(ViewMatchers.withContentDescription("Navigate up"))) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuThemeRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuThemeRobot.kt index a3b425a10..0acfe1f1d 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuThemeRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuThemeRobot.kt @@ -36,6 +36,10 @@ class SettingsSubMenuThemeRobot { fun selectLightMode() = lightModeToggle().click() + fun clickTopToolbarToggle() = topToolbarToggle().click() + + fun clickBottomToolbarToggle() = bottomToolbarToggle().click() + class Transition { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) @@ -62,6 +66,10 @@ private fun darkModeToggle() = onView(withText("Dark")) private fun lightModeToggle() = onView(withText("Light")) +private fun topToolbarToggle() = onView(withText("Top")) + +private fun bottomToolbarToggle() = onView(withText("Bottom")) + private fun deviceModeToggle(): ViewInteraction { val followDeviceThemeText = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) "Follow device theme" else "Set by Battery Saver" diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsTurnOnSyncRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsTurnOnSyncRobot.kt index 5faa2840b..70c00dbb5 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsTurnOnSyncRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsTurnOnSyncRobot.kt @@ -46,13 +46,13 @@ class SettingsTurnOnSyncRobot { } private fun goBackButton() = - Espresso.onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up"))) + Espresso.onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up"))) private fun assertUseEmailField() = Espresso.onView(ViewMatchers.withText("Use email instead")) - .check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + .check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) private fun assertReadyToScan() = Espresso.onView(ViewMatchers.withText("Ready to scan")) - .check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) + .check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) private fun useEmailButton() = Espresso.onView(ViewMatchers.withText("Use email instead")) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SyncedTabsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SyncedTabsRobot.kt deleted file mode 100644 index 43aad7ccf..000000000 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SyncedTabsRobot.kt +++ /dev/null @@ -1,46 +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.ui.robots - -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.withContentDescription -import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility -import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.espresso.matcher.ViewMatchers.Visibility -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiDevice -import org.hamcrest.CoreMatchers.allOf -import org.mozilla.fenix.R -import org.mozilla.fenix.helpers.click - -/** - * Implementation of Robot Pattern for Synced Tabs sub menu. - */ -class SyncedTabsRobot { - - fun verifySyncedTabsMenuHeader() = assertSyncedTabsMenuHeader() - - class Transition { - val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())!! - - fun goBack(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { - goBackButton().click() - - BrowserRobot().interact() - return BrowserRobot.Transition() - } - } -} - -private fun goBackButton() = - onView(allOf(withContentDescription("Navigate up"))) - -private fun assertSyncedTabsMenuHeader() { - // Replaced with the new string here, the test is assuming we are NOT signed in - // Sync tests in SettingsSyncTest are still TO-DO, so I'm not sure that we have a test for signing into Sync - onView(withText(R.string.sync_menu_sign_in)) - .check((matches(withEffectiveVisibility(Visibility.VISIBLE)))) -} diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SystemSettingsRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SystemSettingsRobot.kt index daf9ae8c7..03a1796e8 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SystemSettingsRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SystemSettingsRobot.kt @@ -39,7 +39,8 @@ fun systemSettings(interact: SystemSettingsRobot.() -> Unit): SystemSettingsRobo private fun assertSystemNotificationsView() { mDevice.findObject(UiSelector().resourceId("com.android.settings:id/list")) .waitForExists(waitingTime) - assertTrue(mDevice.findObject(UiSelector().textContains("Show notifications")) - .waitForExists(waitingTime) + assertTrue( + mDevice.findObject(UiSelector().textContains("Show notifications")) + .waitForExists(waitingTime) ) } 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 8a9256817..d74a23236 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 @@ -18,7 +18,7 @@ import androidx.test.espresso.ViewAction import androidx.test.espresso.action.GeneralLocation import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.action.ViewActions.longClick import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions @@ -36,6 +36,7 @@ import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.Until import androidx.test.uiautomator.Until.findObject import com.google.android.material.bottomsheet.BottomSheetBehavior +import junit.framework.TestCase.assertTrue import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.anyOf import org.hamcrest.CoreMatchers.containsString @@ -60,7 +61,7 @@ class TabDrawerRobot { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) mDevice.waitNotNull( - Until.findObject(By.res("org.mozilla.fenix.debug:id/mozac_browser_tabstray_url")), + Until.findObject(By.res("$packageName:id/mozac_browser_tabstray_url")), waitingTime ) onView(withId(R.id.mozac_browser_tabstray_url)) @@ -88,7 +89,7 @@ class TabDrawerRobot { fun closeTab() { mDevice.findObject( - UiSelector().resourceId("org.mozilla.fenix.debug:id/mozac_browser_tabstray_close") + UiSelector().resourceId("$packageName:id/mozac_browser_tabstray_close") ).waitForExists(waitingTime) var retries = 0 // number of retries before failing, will stop at 2 @@ -96,7 +97,7 @@ class TabDrawerRobot { closeTabButton().click() retries++ } while (mDevice.findObject( - UiSelector().resourceId("org.mozilla.fenix.debug:id/mozac_browser_tabstray_close") + UiSelector().resourceId("$packageName:id/mozac_browser_tabstray_close") ).exists() && retries < 3 ) } @@ -150,7 +151,7 @@ class TabDrawerRobot { mDevice.waitNotNull( findObject( By - .res("org.mozilla.fenix.debug:id/play_pause_button") + .res("$packageName:id/play_pause_button") .desc(action) ), waitingTime @@ -173,8 +174,6 @@ class TabDrawerRobot { selectTabsButton.click() } - fun clickAddNewCollection() = addNewCollectionButton().click() - fun selectTab(title: String) { mDevice.waitNotNull( findObject(text(title)), @@ -185,11 +184,14 @@ class TabDrawerRobot { tab.click() } - fun clickSaveCollection() = saveTabsToCollectionButton().click() + fun longClickTab(title: String) { + mDevice.waitNotNull( + findObject(text(title)), + waitingTime + ) - fun typeCollectionName(collectionName: String) { - collectionNameTextField().perform(replaceText(collectionName)) - mDevice.findObject(UiSelector().textContains("OK")).click() + val tab = onView(withText(title)) + tab.perform(longClick()) } fun createCollection( @@ -199,10 +201,19 @@ class TabDrawerRobot { ) { clickSelectTabs() selectTab(tabTitle) - clickSaveCollection() - if (!firstCollection) - clickAddNewCollection() - typeCollectionName(collectionName) + tabDrawer { + }.clickSaveCollection { + if (!firstCollection) + clickAddNewCollection() + typeCollectionNameAndSave(collectionName) + } + } + + fun verifyTabsMultiSelectionCounter(numOfTabs: Int) { + assertTrue( + mDevice.findObject(UiSelector().text("$numOfTabs selected")) + .waitForExists(waitingTime) + ) } class Transition { @@ -219,8 +230,10 @@ class TabDrawerRobot { fun openTabDrawer(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition { mDevice.waitForIdle(waitingTime) tabsCounter().click() - mDevice.waitNotNull(Until.findObject(By.res("$packageName:id/tab_layout")), - waitingTime) + mDevice.waitNotNull( + Until.findObject(By.res("$packageName:id/tab_layout")), + waitingTime + ) TabDrawerRobot().interact() return TabDrawerRobot.Transition() @@ -327,7 +340,7 @@ class TabDrawerRobot { } fun openRecentlyClosedTabs(interact: RecentlyClosedTabsRobot.() -> Unit): - RecentlyClosedTabsRobot.Transition { + RecentlyClosedTabsRobot.Transition { threeDotMenu().click() @@ -343,6 +356,14 @@ class TabDrawerRobot { RecentlyClosedTabsRobot().interact() return RecentlyClosedTabsRobot.Transition() } + + fun clickSaveCollection(interact: CollectionRobot.() -> Unit): + CollectionRobot.Transition { + saveTabsToCollectionButton().click() + + CollectionRobot().interact() + return CollectionRobot.Transition() + } } } @@ -376,9 +397,11 @@ private fun threeDotMenu() = onView(withId(R.id.tab_tray_overflow)) private fun assertExistingOpenTabs(title: String) { try { - mDevice.findObject(UiSelector() - .resourceId("$packageName:id/mozac_browser_tabstray_title") - .textContains(title)) + mDevice.findObject( + UiSelector() + .resourceId("$packageName:id/mozac_browser_tabstray_title") + .textContains(title) + ) .waitForExists(waitingTime) tab(title).check(matches(isDisplayed())) @@ -464,8 +487,4 @@ private fun tabsCounter() = onView(withId(R.id.tab_button)) private fun visibleOrGone(visibility: Boolean) = if (visibility) ViewMatchers.Visibility.VISIBLE else ViewMatchers.Visibility.GONE -private fun addNewCollectionButton() = onView(withId(R.id.add_new_collection)) - private fun saveTabsToCollectionButton() = onView(withId(R.id.collect_multi_select)) - -private fun collectionNameTextField() = onView(withId(R.id.collection_name)) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuHistoryItemRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuHistoryItemRobot.kt index 850a9cec9..46af9d03b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuHistoryItemRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/ThreeDotMenuHistoryItemRobot.kt @@ -34,7 +34,8 @@ class ThreeDotMenuHistoryItemRobot { mDevice.waitNotNull( Until.findObject( By.text("ALL ACTIONS") - ), TestAssetHelper.waitingTime + ), + TestAssetHelper.waitingTime ) LibrarySubMenusMultipleSelectionToolbarRobot().interact() 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 9db6eef94..05991877d 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 @@ -8,7 +8,6 @@ package org.mozilla.fenix.ui.robots import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.swipeDown import androidx.test.espresso.action.ViewActions.swipeUp import androidx.test.espresso.assertion.ViewAssertions.matches @@ -33,6 +32,7 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.mozilla.fenix.R import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime +import org.mozilla.fenix.helpers.TestHelper.packageName import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.ext.waitNotNull import org.mozilla.fenix.share.ShareFragment @@ -50,7 +50,6 @@ class ThreeDotMenuMainRobot { fun verifyAddOnsButton() = assertAddOnsButton() fun verifyHistoryButton() = assertHistoryButton() fun verifyBookmarksButton() = assertBookmarksButton() - fun verifySyncedTabsButton() = assertSyncedTabsButton() fun verifySyncSignInButton() = assertSignInToSyncButton() fun verifyHelpButton() = assertHelpButton() fun verifyThreeDotMenuExists() = threeDotMenuRecyclerViewExists() @@ -80,15 +79,6 @@ class ThreeDotMenuMainRobot { fun verifySaveCollection() = assertSaveCollectionButton() fun verifySelectTabs() = assertSelectTabsButton() - fun clickSaveCollectionButton() { - browserViewSaveCollectionButton().click() - } - - fun clickAddNewCollection() { - addNewCollectionButton().click() - } - - fun verifyCollectionNameTextField() = assertCollectionNameTextField() fun verifyFindInPageButton() = assertFindInPageButton() fun verifyShareScrim() = assertShareScrim() fun verifySendToDeviceTitle() = assertSendToDeviceTitle() @@ -137,11 +127,11 @@ class ThreeDotMenuMainRobot { private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) fun openSettings(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition { - var maxSwipes = 3 - while (!settingsButton().exists() && maxSwipes != 0) { - threeDotMenuRecyclerView().perform(swipeUp()) - maxSwipes-- - } + // We require one swipe to display the full size 3-dot menu. On smaller devices + // such as the Pixel 2, we require two swipes to display the "Settings" menu item + // at the bottom. On larger devices, the second swipe is a no-op. + threeDotMenuRecyclerView().perform(swipeUp()) + threeDotMenuRecyclerView().perform(swipeUp()) settingsButton().click() SettingsRobot().interact() @@ -156,15 +146,6 @@ class ThreeDotMenuMainRobot { return DownloadRobot.Transition() } - fun openSyncedTabs(interact: SyncedTabsRobot.() -> Unit): SyncedTabsRobot.Transition { - onView(withId(R.id.mozac_browser_menu_recyclerView)).perform(swipeDown()) - mDevice.waitNotNull(Until.findObject(By.text("Synced tabs")), waitingTime) - syncedTabsButton().click() - - SyncedTabsRobot().interact() - return SyncedTabsRobot.Transition() - } - fun openSyncSignIn(interact: SyncSignInRobot.() -> Unit): SyncSignInRobot.Transition { onView(withId(R.id.mozac_browser_menu_recyclerView)).perform(swipeDown()) mDevice.waitNotNull(Until.findObject(By.text("Sign in to sync")), waitingTime) @@ -179,7 +160,7 @@ class ThreeDotMenuMainRobot { mDevice.waitNotNull(Until.findObject(By.text("Bookmarks")), waitingTime) bookmarksButton().click() - assertTrue(mDevice.findObject(UiSelector().resourceId("org.mozilla.fenix.debug:id/bookmark_list")).waitForExists(waitingTime)) + assertTrue(mDevice.findObject(UiSelector().resourceId("$packageName:id/bookmark_list")).waitForExists(waitingTime)) BookmarksRobot().interact() return BookmarksRobot.Transition() @@ -296,26 +277,6 @@ class ThreeDotMenuMainRobot { return BrowserRobot.Transition() } - fun typeCollectionName( - name: String, - interact: BrowserRobot.() -> Unit - ): BrowserRobot.Transition { - mDevice.wait( - Until.findObject(By.res("org.mozilla.fenix.debug:id/name_collection_edittext")), - waitingTime - ) - - collectionNameTextField().perform( - ViewActions.replaceText(name), - ViewActions.pressImeActionButton() - ) - // wait for the collection creation wrapper to be dismissed - mDevice.waitNotNull(Until.gone(By.res("org.mozilla.fenix.debug:id/createCollectionWrapper"))) - - BrowserRobot().interact() - return BrowserRobot.Transition() - } - fun openReaderViewAppearance(interact: ReaderViewRobot.() -> Unit): ReaderViewRobot.Transition { var maxSwipes = 3 while (!readerViewAppearanceToggle().exists() && maxSwipes != 0) { @@ -355,15 +316,7 @@ class ThreeDotMenuMainRobot { return AddToHomeScreenRobot.Transition() } - fun selectExistingCollection(title: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { - mDevice.waitNotNull(Until.findObject(By.text(title)), waitingTime) - onView(withText(title)).click() - - BrowserRobot().interact() - return BrowserRobot.Transition() - } - - fun openSaveToCollection(interact: ThreeDotMenuMainRobot.() -> Unit): ThreeDotMenuMainRobot.Transition { + fun openSaveToCollection(interact: CollectionRobot.() -> Unit): CollectionRobot.Transition { // Ensure the menu is expanded and fully scrolled to the bottom. for (i in 0..3) { threeDotMenuRecyclerView().perform(swipeUp()) @@ -371,8 +324,8 @@ class ThreeDotMenuMainRobot { mDevice.waitNotNull(Until.findObject(By.text("Save to collection")), waitingTime) saveCollectionButton().click() - ThreeDotMenuMainRobot().interact() - return ThreeDotMenuMainRobot.Transition() + CollectionRobot().interact() + return CollectionRobot.Transition() } fun openAddonsManagerMenu(interact: SettingsSubMenuAddonsManagerRobot.() -> Unit): SettingsSubMenuAddonsManagerRobot.Transition { @@ -384,13 +337,6 @@ class ThreeDotMenuMainRobot { SettingsSubMenuAddonsManagerRobot().interact() return SettingsSubMenuAddonsManagerRobot.Transition() } - - fun exitSaveCollection(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { - exitSaveCollectionButton().click() - - BrowserRobot().interact() - return BrowserRobot.Transition() - } } } private fun threeDotMenuRecyclerView() = @@ -417,10 +363,6 @@ private fun bookmarksButton() = onView(allOf(withText(R.string.library_bookmarks private fun assertBookmarksButton() = bookmarksButton() .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) -private fun syncedTabsButton() = onView(allOf(withText(R.string.library_synced_tabs))) -private fun assertSyncedTabsButton() = syncedTabsButton() - .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) - private fun signInToSyncButton() = onView(withText("Sign in to sync")) private fun assertSignInToSyncButton() = signInToSyncButton().check(matches(isDisplayed())) @@ -457,13 +399,6 @@ private fun assertShareTabButton() = shareTabButton() private fun shareButton() = mDevice.findObject(UiSelector().description("Share")) private fun assertShareButton() = assertTrue(shareButton().waitForExists(waitingTime)) -private fun browserViewSaveCollectionButton() = onView( - allOf( - withText("Save to collection"), - withEffectiveVisibility(Visibility.VISIBLE) - ) -) - private fun saveCollectionButton() = onView(allOf(withText("Save to collection"))).inRoot(RootMatchers.isPlatformPopup()) private fun assertSaveCollectionButton() = saveCollectionButton() .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) @@ -472,16 +407,6 @@ private fun selectTabsButton() = onView(allOf(withText("Select tabs"))).inRoot(R private fun assertSelectTabsButton() = selectTabsButton() .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) -private fun addNewCollectionButton() = onView(allOf(withText("Add new collection"))) -private fun assertaddNewCollectionButton() = addNewCollectionButton() - .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) - -private fun collectionNameTextField() = - onView(allOf(withResourceName("name_collection_edittext"))) - -private fun assertCollectionNameTextField() = collectionNameTextField() - .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) - private fun reportSiteIssueButton() = onView(withText("Report Site Issue…")) private fun assertReportSiteIssueButton() = reportSiteIssueButton().check(matches(isDisplayed())) @@ -581,15 +506,14 @@ private fun clickAddonsManagerButton() { addOnsButton().check(matches(isCompletelyDisplayed())).click() } -private fun exitSaveCollectionButton() = onView(withId(R.id.back_button)).check(matches(isDisplayed())) - private fun tabSettingsButton() = onView(allOf(withText("Tab settings"))).inRoot(RootMatchers.isPlatformPopup()) private fun assertTabSettingsButton() { tabSettingsButton() .check( - matches(isDisplayed())) + matches(isDisplayed()) + ) } private fun recentlyClosedTabsButton() = @@ -598,7 +522,8 @@ private fun recentlyClosedTabsButton() = private fun assertRecentlyClosedTabsButton() { recentlyClosedTabsButton() .check( - matches(isDisplayed())) + matches(isDisplayed()) + ) } private fun shareAllTabsButton() = @@ -607,7 +532,8 @@ private fun shareAllTabsButton() = private fun assertShareAllTabsButton() { shareAllTabsButton() .check( - matches(isDisplayed())) + matches(isDisplayed()) + ) } private fun assertNewTabButton() = onView(withText("New tab")).check(matches(isDisplayed())) diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/util/Strings.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/util/Strings.kt index 3550cf1bc..d7448ca56 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/util/Strings.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/util/Strings.kt @@ -7,3 +7,6 @@ package org.mozilla.fenix.ui.util const val STRING_ONBOARDING_ACCOUNT_SIGN_IN_HEADER = "Sync Firefox between devices" const val STRING_ONBOARDING_TRACKING_PROTECTION_HEADER = "Always-on privacy" const val STRING_ONBOARDING_TOOLBAR_PLACEMENT_HEADER = "Pick your toolbar placement" +const val FRENCH_LANGUAGE_HEADER = "Langues" +const val ROMANIAN_LANGUAGE_HEADER = "Limbă" +const val FRENCH_SYSTEM_LOCALE_OPTION = "Utiliser la langue de l’appareil" diff --git a/app/src/beta/AndroidManifest.xml b/app/src/beta/AndroidManifest.xml new file mode 100644 index 000000000..f98b6fa2f --- /dev/null +++ b/app/src/beta/AndroidManifest.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml index 0b66b4956..e46288934 100644 --- a/app/src/debug/AndroidManifest.xml +++ b/app/src/debug/AndroidManifest.xml @@ -22,12 +22,16 @@ + android:theme="@style/Theme.AppCompat.Translucent" /> + + , - trackingProtectionPolicy: TrackingProtectionPolicy - ): GeckoRuntime { - if (runtime == null) { - runtime = createRuntime(context, storage, trackingProtectionPolicy) - } - - return runtime!! - } - - private fun createRuntime( - context: Context, - storage: Lazy, - policy: TrackingProtectionPolicy - ): GeckoRuntime { - val builder = GeckoRuntimeSettings.Builder() - - testConfig?.let { - builder.extras(it) - .remoteDebuggingEnabled(true) - } - - // Use meeee. - policy.hashCode() - - val runtimeSettings = builder - .crashHandler(CrashHandlerService::class.java) - .telemetryDelegate(GeckoAdapter()) - .contentBlocking(policy.toContentBlockingSetting()) - .aboutConfigEnabled(true) - .debugLogging(Config.channel.isDebug) - .build() - - val settings = context.components.settings - if (!settings.shouldUseAutoSize) { - runtimeSettings.automaticFontSizeAdjustment = false - val fontSize = settings.fontSizeFactor - runtimeSettings.fontSizeFactor = fontSize - } - - // Add safebrowsing providers for China - if (Config.channel.isMozillaOnline) { - val mozcn = SafeBrowsingProvider - .withName("mozcn") - .version("2.2") - .lists("m6eb-phish-shavar", "m6ib-phish-shavar") - .updateUrl(CN_UPDATE_URL) - .getHashUrl(CN_GET_HASH_URL) - .build() - - runtimeSettings.contentBlocking.setSafeBrowsingProviders(mozcn, - // Keep the existing configuration - ContentBlocking.GOOGLE_SAFE_BROWSING_PROVIDER, - ContentBlocking.GOOGLE_LEGACY_SAFE_BROWSING_PROVIDER) - - runtimeSettings.contentBlocking.setSafeBrowsingPhishingTable( - "m6eb-phish-shavar", - "m6ib-phish-shavar", - // Existing configuration - "goog-phish-proto") - } - - val geckoRuntime = GeckoRuntime.create(context, runtimeSettings) - val loginStorageDelegate = GeckoLoginStorageDelegate(storage) - @Suppress("Deprecation") - geckoRuntime.loginStorageDelegate = GeckoLoginDelegateWrapper(loginStorageDelegate) - - return geckoRuntime - } -} diff --git a/app/src/main/assets/extensions/ads/ads.js b/app/src/main/assets/extensions/ads/ads.js deleted file mode 100644 index 356bda2d2..000000000 --- a/app/src/main/assets/extensions/ads/ads.js +++ /dev/null @@ -1,61 +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/. */ - -const ADLINK_CHECK_TIMEOUT_MS = 1000; - -function collectLinks(urls) { - let anchors = document.getElementsByTagName("a"); - for (let anchor of anchors) { - if (!anchor.href) { - continue; - } - urls.push(anchor.href); - } -} - -function sendLinks(cookies) { - let urls = []; - collectLinks(urls); - - let message = { - 'url': document.location.href, - 'urls': urls, - 'cookies': cookies - }; - browser.runtime.sendNativeMessage("MozacBrowserAds", message); -} - -function notify(message) { - sendLinks(message.cookies); -} - -browser.runtime.onMessage.addListener(notify); - -const events = ["pageshow", "load", "unload"]; -var timeout; - -const eventLogger = event => { - switch (event.type) { - case "load": - timeout = setTimeout(() => { - browser.runtime.sendMessage({ "checkCookies": true }); - }, ADLINK_CHECK_TIMEOUT_MS) - break; - case "pageshow": - if (event.persisted) { - timeout = setTimeout(() => { - browser.runtime.sendMessage({ "checkCookies": true }); - }, ADLINK_CHECK_TIMEOUT_MS) - } - break; - case "unload": - clearTimeout(timeout); - default: - console.log('Event:', event.type); - } -}; - -events.forEach(eventName => - window.addEventListener(eventName, eventLogger) -); diff --git a/app/src/main/assets/extensions/ads/adsBackground.js b/app/src/main/assets/extensions/ads/adsBackground.js deleted file mode 100644 index 63b6f0fb9..000000000 --- a/app/src/main/assets/extensions/ads/adsBackground.js +++ /dev/null @@ -1,28 +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/. */ - -browser.runtime.onMessage.addListener(notify); - -function sendMessageToTabs(tabs, cookies) { - for (let tab of tabs) { - browser.tabs.sendMessage( - tab.id, - { cookies } - ); - } -} - -function notify(message) { - if (message.checkCookies) { - browser.cookies.getAll({}) - .then(cookies => { - browser.tabs.query({ - currentWindow: true, - active: true - }).then(tabs => { - sendMessageToTabs(tabs, cookies); - }); - }); - } -} diff --git a/app/src/main/assets/extensions/ads/manifest.template.json b/app/src/main/assets/extensions/ads/manifest.template.json deleted file mode 100644 index 8f4ef48f6..000000000 --- a/app/src/main/assets/extensions/ads/manifest.template.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "manifest_version": 2, - "applications": { - "gecko": { - "id": "ads@mozac.org" - } - }, - "name": "Mozilla Android Components - Ads", - "version": "${version}", - "content_scripts": [ - { - "matches": ["https://*/*"], - "include_globs": [ - "https://www.google.*/search*", - "https://www.bing.com/search*", - "https://duckduckgo.com/*" - ], - "js": ["ads.js"], - "run_at": "document_end" - } - ], - "background": { - "scripts": ["adsBackground.js"] - }, - "permissions": [ - "geckoViewAddons", - "nativeMessaging", - "nativeMessagingFromContent", - "geckoViewAddons", - "nativeMessaging", - "nativeMessagingFromContent", - "webNavigation", - "webRequest", - "webRequestBlocking", - "cookies", - "*://*/*" - ] -} diff --git a/app/src/main/assets/extensions/cookies/cookies.js b/app/src/main/assets/extensions/cookies/cookies.js deleted file mode 100644 index d5ac33bd2..000000000 --- a/app/src/main/assets/extensions/cookies/cookies.js +++ /dev/null @@ -1,47 +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/. */ - -const COOKIES_CHECK_TIMEOUT_MS = 1000; - -function sendCookies(cookies) { - let message = { - 'url': document.location.href, - 'cookies': cookies - } - browser.runtime.sendNativeMessage("BrowserCookiesMessage", message); -} - -function notify(message) { - sendCookies(message.cookies); -} - -browser.runtime.onMessage.addListener(notify); - -const events = ["pageshow", "load", "unload"]; -var timeout; - -const eventLogger = event => { - switch (event.type) { - case "load": - timeout = setTimeout(() => { - browser.runtime.sendMessage({"checkCookies": true}); - }, COOKIES_CHECK_TIMEOUT_MS); - break; - case "pageshow": - if (event.persisted) { - timeout = setTimeout(() => { - browser.runtime.sendMessage({"checkCookies": true}); - }, COOKIES_CHECK_TIMEOUT_MS); - } - break; - case "unload": - clearTimeout(timeout); - default: - console.log('Event:', event.type); - } -}; - -events.forEach(eventName => - window.addEventListener(eventName, eventLogger) -); diff --git a/app/src/main/assets/extensions/cookies/cookiesBackground.js b/app/src/main/assets/extensions/cookies/cookiesBackground.js deleted file mode 100644 index 755eb6909..000000000 --- a/app/src/main/assets/extensions/cookies/cookiesBackground.js +++ /dev/null @@ -1,28 +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/. */ - -browser.runtime.onMessage.addListener(notify); - -function sendMessageToTabs(tabs, cookies) { - for (let tab of tabs) { - browser.tabs.sendMessage( - tab.id, - {cookies: cookies} - ); - } -} - -function notify(message) { - if(message.checkCookies) { - browser.cookies.getAll({}) - .then(cookies => { - browser.tabs.query({ - currentWindow: true, - active: true - }).then(tabs => { - sendMessageToTabs(tabs, cookies); - }); - }); - } -} \ No newline at end of file diff --git a/app/src/main/assets/extensions/cookies/manifest.template.json b/app/src/main/assets/extensions/cookies/manifest.template.json deleted file mode 100644 index 06440bccf..000000000 --- a/app/src/main/assets/extensions/cookies/manifest.template.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "manifest_version": 2, - "applications": { - "gecko": { - "id": "cookies@mozac.org" - } - }, - "name": "Mozilla Android Components - Cookies", - "version": "${version}", - "content_scripts": [ - { - "matches": ["https://*/*"], - "include_globs": [ - "https://www.google.*/search*", - "https://www.baidu.com/from=844b/s*", - "https://www.baidu.com/from=844b/baidu*", - "https://*search.yahoo.com/search*", - "https://www.bing.com/search*", - "https://duckduckgo.com/*" - ], - "js": ["cookies.js"], - "run_at": "document_end" - } - ], - "background": { - "scripts": ["cookiesBackground.js"] - }, - "permissions": [ - "geckoViewAddons", - "nativeMessaging", - "nativeMessagingFromContent", - "webNavigation", - "webRequest", - "webRequestBlocking", - "cookies", - "*://*/*" - ] -} diff --git a/app/src/main/java/com/adjust/sdk/Adjust.java b/app/src/main/java/com/adjust/sdk/Adjust.java index e219144db..5dc3617fa 100644 --- a/app/src/main/java/com/adjust/sdk/Adjust.java +++ b/app/src/main/java/com/adjust/sdk/Adjust.java @@ -37,4 +37,7 @@ public class Adjust { public static void setEnabled(boolean enabled) { } + + public static void gdprForgetMe(Object ignored) { + } } diff --git a/app/src/main/java/org/mozilla/fenix/AppRequestInterceptor.kt b/app/src/main/java/org/mozilla/fenix/AppRequestInterceptor.kt index f026bf3bc..1e4d8926a 100644 --- a/app/src/main/java/org/mozilla/fenix/AppRequestInterceptor.kt +++ b/app/src/main/java/org/mozilla/fenix/AppRequestInterceptor.kt @@ -15,7 +15,6 @@ import mozilla.components.concept.engine.request.RequestInterceptor import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.isOnline -import org.mozilla.fenix.ext.navigateBlockingForAsyncNavGraph import java.lang.ref.WeakReference class AppRequestInterceptor( @@ -98,7 +97,7 @@ class AppRequestInterceptor( // Navigate and trigger add-on installation. matchResult.groupValues.getOrNull(1)?.let { addonId -> - navController?.get()?.navigateBlockingForAsyncNavGraph( + navController?.get()?.navigate( NavGraphDirections.actionGlobalAddonsManagementFragment(addonId) ) diff --git a/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt b/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt index 6b3734e59..113001daa 100644 --- a/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt +++ b/app/src/main/java/org/mozilla/fenix/BrowserDirection.kt @@ -18,7 +18,6 @@ enum class BrowserDirection(@IdRes val fragmentId: Int) { FromHome(R.id.homeFragment), FromSearchDialog(R.id.searchDialogFragment), FromSettings(R.id.settingsFragment), - FromSyncedTabs(R.id.syncedTabsFragment), FromBookmarks(R.id.bookmarkFragment), FromHistory(R.id.historyFragment), FromTrackingProtectionExceptions(R.id.trackingProtectionExceptionsFragment), @@ -31,7 +30,6 @@ enum class BrowserDirection(@IdRes val fragmentId: Int) { FromAddonDetailsFragment(R.id.addonDetailsFragment), FromAddonPermissionsDetailsFragment(R.id.addonPermissionsDetailFragment), FromLoginDetailFragment(R.id.loginDetailFragment), - FromTabTrayDialog(R.id.tabTrayDialogFragment), - FromTabTray(R.id.tabsTrayFragment), + FromTabsTray(R.id.tabsTrayFragment), FromRecentlyClosed(R.id.recentlyClosedFragment) } diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index ec81c9482..fa0905645 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -14,11 +14,6 @@ object FeatureFlags { */ const val pullToRefreshEnabled = true - /** - * Enables the Nimbus experiments library. - */ - const val nimbusExperiments = false - /** * Enables the Addresses autofill feature. */ @@ -35,12 +30,45 @@ object FeatureFlags { const val webAuthFeature = true /** - * Shows new three-dot toolbar menu design. + * Enables the Home button in the browser toolbar to navigate back to the home screen. + */ + val showHomeButtonFeature = Config.channel.isNightlyOrDebug + + /** + * Enables the Start On Home feature in the settings page. + */ + val showStartOnHomeSettings = Config.channel.isNightlyOrDebug + + /** + * Enables the "recent" tabs feature in the home screen. + */ + val showRecentTabsFeature = Config.channel.isNightlyOrDebug + + /** + * Enables recording of history metadata. + */ + val historyMetadataFeature = Config.channel.isDebug + + /** + * Enables the recently saved bookmarks feature in the home screen. + */ + val recentBookmarksFeature = Config.channel.isNightlyOrDebug + + /** + * Identifies and separates the tabs list with a secondary section containing least used tabs. + */ + val inactiveTabs = Config.channel.isNightlyOrDebug + + /** + * Enables support for Android Autofill. + * + * In addition to toggling this flag, matching entries in the Android Manifest of the build + * type need to present. */ - const val toolbarMenuFeature = true + val androidAutofill = Config.channel.isNightlyOrDebug || Config.channel.isBeta /** - * Enables the tabs tray re-write with Synced Tabs. + * Enables showing the home screen behind the search dialog */ - const val tabsTrayRewrite = true + val showHomeBehindSearch = Config.channel.isNightlyOrDebug } diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index f613fac45..d8133a270 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -17,6 +17,7 @@ import androidx.lifecycle.ProcessLifecycleOwner import androidx.work.Configuration.Builder import androidx.work.Configuration.Provider import kotlinx.coroutines.Deferred +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async @@ -64,10 +65,12 @@ import org.mozilla.fenix.telemetry.TelemetryLifecycleObserver import org.mozilla.fenix.utils.BrowsersCache import java.util.concurrent.TimeUnit import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.feature.autofill.AutofillUseCases import mozilla.components.feature.search.ext.buildSearchUrl import mozilla.components.feature.search.ext.waitForSelectedOrDefaultSearchEngine import mozilla.components.service.fxa.manager.SyncEnginesStorage import org.mozilla.fenix.GleanMetrics.Addons +import org.mozilla.fenix.GleanMetrics.AndroidAutofill import org.mozilla.fenix.GleanMetrics.Preferences import org.mozilla.fenix.GleanMetrics.SearchDefaultEngine import org.mozilla.fenix.components.metrics.Event @@ -125,6 +128,7 @@ open class FenixApplication : LocaleAwareApplication(), Provider { PerfStartup.applicationOnCreate.stopAndAccumulate(completeMethodDurationTimerId) } + @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage protected open fun initializeGlean() { val telemetryEnabled = settings().isTelemetryEnabled @@ -136,7 +140,8 @@ open class FenixApplication : LocaleAwareApplication(), Provider { channel = BuildConfig.BUILD_TYPE, httpClient = ConceptFetchHttpUploader( lazy(LazyThreadSafetyMode.NONE) { components.core.client } - )), + ) + ), uploadEnabled = telemetryEnabled, buildInfo = GleanBuildInfo.buildInfo ) @@ -205,11 +210,10 @@ open class FenixApplication : LocaleAwareApplication(), Provider { initVisualCompletenessQueueAndQueueTasks() ProcessLifecycleOwner.get().lifecycle.addObserver(TelemetryLifecycleObserver(components.core.store)) - - components.appStartupTelemetry.onFenixApplicationOnCreate() } } + @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage private fun restoreBrowserState() = GlobalScope.launch(Dispatchers.Main) { val store = components.core.store val sessionStorage = components.core.sessionStorage @@ -235,6 +239,7 @@ open class FenixApplication : LocaleAwareApplication(), Provider { registerActivityLifecycleCallbacks(PerformanceActivityLifecycleCallbacks(queue)) } + @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage fun queueInitStorageAndServices() { components.performance.visualCompletenessQueue.queue.runIfReadyOrQueue { GlobalScope.launch(Dispatchers.IO) { @@ -268,12 +273,14 @@ open class FenixApplication : LocaleAwareApplication(), Provider { } } + @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage fun queueReviewPrompt() { GlobalScope.launch(Dispatchers.IO) { components.reviewPromptController.trackApplicationLaunch() } } + @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage fun queueRestoreLocale() { components.performance.visualCompletenessQueue.queue.runIfReadyOrQueue { GlobalScope.launch(Dispatchers.IO) { @@ -306,6 +313,8 @@ open class FenixApplication : LocaleAwareApplication(), Provider { // To re-enable this, we need to do so in a way that won't interfere with any startup operations // which acquire reserved+ sqlite lock. Currently, Fennec migrations need to write to storage // on startup, and since they run in a background service we can't simply order these operations. + + @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage private fun runStorageMaintenance() { GlobalScope.launch(Dispatchers.IO) { // Bookmarks and history storage sit on top of the same db file so we only need to @@ -360,6 +369,7 @@ open class FenixApplication : LocaleAwareApplication(), Provider { * - https://github.com/mozilla/application-services/blob/master/docs/design/megazords.md * - https://mozilla.github.io/application-services/docs/applications/consuming-megazord-libraries.html */ + @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage private fun setupMegazord(): Deferred { // Note: Megazord.init() must be called as soon as possible ... Megazord.init() @@ -383,15 +393,17 @@ open class FenixApplication : LocaleAwareApplication(), Provider { logger.info("onTrimMemory(), level=$level, main=${isMainProcess()}") - components.analytics.crashReporter.recordCrashBreadcrumb(Breadcrumb( - category = "Memory", - message = "onTrimMemory()", - data = mapOf( - "level" to level.toString(), - "main" to isMainProcess().toString() - ), - level = Breadcrumb.Level.INFO - )) + components.analytics.crashReporter.recordCrashBreadcrumb( + Breadcrumb( + category = "Memory", + message = "onTrimMemory()", + data = mapOf( + "level" to level.toString(), + "main" to isMainProcess().toString() + ), + level = Breadcrumb.Level.INFO + ) + ) runOnlyInMainProcess { components.core.icons.onTrimMemory(level) @@ -456,6 +468,7 @@ open class FenixApplication : LocaleAwareApplication(), Provider { } } + @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage private fun warmBrowsersCache() { // We avoid blocking the main thread for BrowsersCache on startup by loading it on // background thread. @@ -478,29 +491,24 @@ open class FenixApplication : LocaleAwareApplication(), Provider { components.core.store, onNewTabOverride = { _, engineSession, url -> - val shouldCreatePrivateSession = - components.core.store.state.selectedTab?.content?.private - ?: components.settings.openLinksInAPrivateTab - - if (shouldCreatePrivateSession) { - components.useCases.tabsUseCases.addPrivateTab( - url = url, - selectTab = true, - engineSession = engineSession - ) - } else { - components.useCases.tabsUseCases.addTab( - url = url, - selectTab = true, - engineSession = engineSession - ) - } + val shouldCreatePrivateSession = + components.core.store.state.selectedTab?.content?.private + ?: components.settings.openLinksInAPrivateTab + + components.useCases.tabsUseCases.addTab( + url = url, + selectTab = true, + engineSession = engineSession, + private = shouldCreatePrivateSession + ) }, onCloseTabOverride = { - _, sessionId -> components.useCases.tabsUseCases.removeTab(sessionId) + _, sessionId -> + components.useCases.tabsUseCases.removeTab(sessionId) }, onSelectTabOverride = { - _, sessionId -> components.useCases.tabsUseCases.selectTab(sessionId) + _, sessionId -> + components.useCases.tabsUseCases.selectTab(sessionId) }, onExtensionsLoaded = { extensions -> components.addonUpdater.registerForFutureUpdates(extensions) @@ -580,18 +588,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider { topSitesCount.add(topSitesSize) } - if (settings.creditCardsSavedCount > 0) { - creditCardsSavedCount.add(settings.creditCardsSavedCount) - } - - if (settings.creditCardsDeletedCount > 0) { - creditCardsDeletedCount.add(settings.creditCardsDeletedCount) - } - - if (settings.creditCardsAutofilledCount > 0) { - creditCardsAutofillCount.add(settings.creditCardsAutofilledCount) - } - val installedAddonSize = settings.installedAddonsCount Addons.hasInstalledAddons.set(installedAddonSize > 0) if (installedAddonSize > 0) { @@ -627,6 +623,12 @@ open class FenixApplication : LocaleAwareApplication(), Provider { closeTabSetting.set(settings.getTabTimeoutPingString()) } + with(AndroidAutofill) { + val autofillUseCases = AutofillUseCases() + supported.set(autofillUseCases.isSupported(applicationContext)) + enabled.set(autofillUseCases.isEnabled(applicationContext)) + } + browserStore.waitForSelectedOrDefaultSearchEngine { searchEngine -> if (searchEngine != null) { SearchDefaultEngine.apply { @@ -650,8 +652,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider { bookmarksSuggestion.set(settings.shouldShowBookmarkSuggestions) clipboardSuggestionsEnabled.set(settings.shouldShowClipboardSuggestions) searchShortcutsEnabled.set(settings.shouldShowSearchShortcuts) - openLinksInPrivate.set(settings.openLinksInAPrivateTab) - privateSearchSuggestions.set(settings.shouldShowSearchSuggestionsInPrivate) voiceSearchEnabled.set(settings.shouldShowVoiceSearch) openLinksInAppEnabled.set(settings.openLinksInExternalApp) signedInSync.set(settings.signedInFxaAccount) diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index 5317b9c2d..af01860f0 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.Intent.ACTION_MAIN import android.content.res.Configuration import android.os.Build import android.os.Bundle @@ -78,7 +79,6 @@ import org.mozilla.fenix.exceptions.trackingprotection.TrackingProtectionExcepti import org.mozilla.fenix.ext.alreadyOnDestination import org.mozilla.fenix.ext.breadcrumb import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.navigateBlockingForAsyncNavGraph import org.mozilla.fenix.ext.measureNoInline import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.nav @@ -86,6 +86,7 @@ import org.mozilla.fenix.ext.setNavigationIcon import org.mozilla.fenix.ext.settings import org.mozilla.fenix.home.HomeFragmentDirections import org.mozilla.fenix.home.intent.CrashReporterIntentProcessor +import org.mozilla.fenix.home.intent.DefaultBrowserIntentProcessor import org.mozilla.fenix.home.intent.OpenBrowserIntentProcessor import org.mozilla.fenix.home.intent.OpenSpecificTabIntentProcessor import org.mozilla.fenix.home.intent.SpeechProcessingIntentProcessor @@ -94,7 +95,7 @@ import org.mozilla.fenix.library.bookmarks.BookmarkFragmentDirections import org.mozilla.fenix.library.bookmarks.DesktopFolders import org.mozilla.fenix.library.history.HistoryFragmentDirections import org.mozilla.fenix.library.recentlyclosed.RecentlyClosedFragmentDirections -import org.mozilla.fenix.perf.NavGraphProvider +import org.mozilla.fenix.onboarding.DefaultBrowserNotificationWorker import org.mozilla.fenix.perf.Performance import org.mozilla.fenix.perf.PerformanceInflater import org.mozilla.fenix.perf.ProfilerMarkers @@ -111,13 +112,12 @@ import org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragmentDirecti import org.mozilla.fenix.settings.search.AddSearchEngineFragmentDirections import org.mozilla.fenix.settings.search.EditCustomSearchEngineFragmentDirections import org.mozilla.fenix.share.AddNewDeviceFragmentDirections -import org.mozilla.fenix.sync.SyncedTabsFragmentDirections +import org.mozilla.fenix.tabstray.TabsTrayFragment import org.mozilla.fenix.tabstray.TabsTrayFragmentDirections -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 org.mozilla.fenix.utils.Settings import java.lang.ref.WeakReference /** @@ -160,7 +160,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { SpeechProcessingIntentProcessor(this, components.core.store, components.analytics.metrics), StartSearchIntentProcessor(components.analytics.metrics), OpenBrowserIntentProcessor(this, ::getIntentSessionId), - OpenSpecificTabIntentProcessor(this) + OpenSpecificTabIntentProcessor(this), + DefaultBrowserIntentProcessor(this, components.analytics.metrics) ) } @@ -199,11 +200,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { components.publicSuffixList.prefetch() - setContentView(R.layout.activity_home).run { - // Do not call anything between setContentView and inflateNavGraphAsync. - // It needs to start its job as early as possible. - NavGraphProvider.inflateNavGraphAsync(navHost.navController, lifecycleScope) - } + setContentView(R.layout.activity_home) // Must be after we set the content view if (isVisuallyComplete) { @@ -219,9 +216,12 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { it.start() } - if (isActivityColdStarted(intent, savedInstanceState) && - !externalSourceIntentProcessors.any { it.process(intent, navHost.navController, this.intent) }) { + if (!shouldStartOnHome() && + shouldNavigateBrowserFragmentOnCouldStart(savedInstanceState) + ) { navigateToBrowserOnColdStart() + } else if (FeatureFlags.showStartOnHomeSettings) { + components.analytics.metrics.track(Event.StartOnHomeEnterHomeScreen) } Performance.processIntentIfPerformanceTest(intent, this) @@ -238,10 +238,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { safeIntent ?.let(::getIntentSource) ?.also { components.analytics.metrics.track(Event.OpenedApp(it)) } - // record on cold startup - safeIntent - ?.let(::getIntentAllSource) - ?.also { components.analytics.metrics.track(Event.AppReceivedIntent(it)) } } supportActionBar?.hide() @@ -257,7 +253,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { captureSnapshotTelemetryMetrics() - startupTelemetryOnCreateCalled(intent.toSafeIntent(), savedInstanceState != null) + startupTelemetryOnCreateCalled(intent.toSafeIntent()) startupPathProvider.attachOnActivityOnCreate(lifecycle, intent) startupTypeTelemetry = StartupTypeTelemetry(components.startupStateProvider, startupPathProvider).apply { attachOnHomeActivityOnCreate(lifecycle) @@ -268,17 +264,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { StartupTimeline.onActivityCreateEndHome(this) // DO NOT MOVE ANYTHING BELOW HERE. } - protected open fun startupTelemetryOnCreateCalled( - safeIntent: SafeIntent, - hasSavedInstanceState: Boolean - ) { - // This function gets overridden by subclasses. - components.appStartupTelemetry.onHomeActivityOnCreate( - safeIntent, - hasSavedInstanceState, - homeActivityInitTimeStampNanoSeconds, rootContainer - ) - + private fun startupTelemetryOnCreateCalled(safeIntent: SafeIntent) { + // We intentionally only record this in HomeActivity and not ExternalBrowserActivity (e.g. + // PWAs) so we don't include more unpredictable code paths in the results. components.performance.coldStartupDurationTelemetry.onHomeActivityOnCreate( components.performance.visualCompletenessQueue, components.startupStateProvider, @@ -287,17 +275,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { ) } - override fun onRestart() { - // DO NOT MOVE ANYTHING ABOVE THIS.. - // we are measuring startup time for hot startup type - startupTelemetryOnRestartCalled() - super.onRestart() - } - - private fun startupTelemetryOnRestartCalled() { - components.appStartupTelemetry.onHomeActivityOnRestart(rootContainer) - } - @CallSuper override fun onResume() { super.onResume() @@ -330,17 +307,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { } } - // Launch this on a background thread so as not to affect startup performance - lifecycleScope.launch(IO) { - if ( - settings().isDefaultBrowser() && - settings().wasDefaultBrowserOnLastResume != settings().isDefaultBrowser() - ) { - metrics.track(Event.ChangedToDefaultBrowser) - } - - settings().wasDefaultBrowserOnLastResume = settings().isDefaultBrowser() - } + isFenixTheDefaultBrowser() } override fun onStart() = PerfStartup.homeActivityOnStart.measureNoInline { @@ -366,8 +333,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { "finishing" to isFinishing.toString() ) ) - - components.appStartupTelemetry.onStop() } final override fun onPause() { @@ -502,19 +467,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { ?.childFragmentManager ?.fragments ?.lastOrNull() - ?.let { it as? TabTrayDialogFragment } + ?.let { it as? TabsTrayFragment } ?.also { it.dismissAllowingStateLoss() } } - - // Note: This does not work in case of an user sending an intent with ACTION_VIEW - // for example, launch the application, and than use adb to send an intent with - // ACTION_VIEW to open a link. In this case, we will get multiple telemetry events. - intent - .toSafeIntent() - .let(::getIntentAllSource) - ?.also { components.analytics.metrics.track(Event.AppReceivedIntent(it)) } - - components.appStartupTelemetry.onHomeActivityOnNewIntent(intent.toSafeIntent()) } /** @@ -585,6 +540,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { super.onBackPressed() } + @Suppress("DEPRECATION") + // https://github.com/mozilla-mobile/fenix/issues/19919 final override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach { if (it is ActivityResultHandler && it.onActivityResult(requestCode, data, resultCode)) { @@ -668,14 +625,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { } } - protected open fun getIntentAllSource(intent: SafeIntent): Event.AppReceivedIntent.Source? { - return when { - intent.isLauncherIntent -> Event.AppReceivedIntent.Source.APP_ICON - intent.action == Intent.ACTION_VIEW -> Event.AppReceivedIntent.Source.LINK - else -> Event.AppReceivedIntent.Source.UNKNOWN - } - } - /** * External sources such as 3rd party links and shortcuts use this function to enter * private mode directly before the content view is created. Returns the mode set by the intent @@ -787,8 +736,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { SearchDialogFragmentDirections.actionGlobalBrowser(customTabSessionId) BrowserDirection.FromSettings -> SettingsFragmentDirections.actionGlobalBrowser(customTabSessionId) - BrowserDirection.FromSyncedTabs -> - SyncedTabsFragmentDirections.actionGlobalBrowser(customTabSessionId) BrowserDirection.FromBookmarks -> BookmarkFragmentDirections.actionGlobalBrowser(customTabSessionId) BrowserDirection.FromHistory -> @@ -813,9 +760,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { AddonPermissionsDetailsFragmentDirections.actionGlobalBrowser(customTabSessionId) BrowserDirection.FromLoginDetailFragment -> LoginDetailFragmentDirections.actionGlobalBrowser(customTabSessionId) - BrowserDirection.FromTabTrayDialog -> - TabTrayDialogFragmentDirections.actionGlobalBrowser(customTabSessionId) - BrowserDirection.FromTabTray -> + BrowserDirection.FromTabsTray -> TabsTrayFragmentDirections.actionGlobalBrowser(customTabSessionId) BrowserDirection.FromRecentlyClosed -> RecentlyClosedFragmentDirections.actionGlobalBrowser(customTabSessionId) @@ -837,28 +782,38 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { val startTime = components.core.engine.profiler?.getProfilerTime() val mode = browsingModeManager.mode - val loadUrlUseCase = if (newTab) { - when (mode) { - BrowsingMode.Private -> components.useCases.tabsUseCases.addPrivateTab - BrowsingMode.Normal -> components.useCases.tabsUseCases.addTab - } - } else components.useCases.sessionUseCases.loadUrl + val private = when (mode) { + BrowsingMode.Private -> true + BrowsingMode.Normal -> false + } // In situations where we want to perform a search but have no search engine (e.g. the user // has removed all of them, or we couldn't load any) we will pass searchTermOrURL to Gecko // and let it try to load whatever was entered. if ((!forceSearch && searchTermOrURL.isUrl()) || engine == null) { - loadUrlUseCase.invoke(searchTermOrURL.toNormalizedUrl(), flags) + val tabId = if (newTab) { + components.useCases.tabsUseCases.addTab( + url = searchTermOrURL.toNormalizedUrl(), + flags = flags, + private = private + ) + } else { + components.useCases.sessionUseCases.loadUrl( + url = searchTermOrURL.toNormalizedUrl(), + flags = flags + ) + components.core.store.state.selectedTabId + } - if (requestDesktopMode) { - handleRequestDesktopMode() + if (requestDesktopMode && tabId != null) { + handleRequestDesktopMode(tabId) } } else { if (newTab) { components.useCases.searchUseCases.newTabSearch .invoke( searchTermOrURL, - SessionState.Source.USER_ENTERED, + SessionState.Source.Internal.UserEntered, true, mode.isPrivate, searchEngine = engine @@ -879,15 +834,10 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { } } - internal fun handleRequestDesktopMode() { - val requestDesktopSiteUseCase = - components.useCases.sessionUseCases.requestDesktopSite - requestDesktopSiteUseCase.invoke(true) - components.core.store.dispatch( - ContentAction.UpdateDesktopModeAction( - components.core.store.state.selectedTabId.toString(), true - ) - ) + internal fun handleRequestDesktopMode(tabId: String) { + components.useCases.sessionUseCases.requestDesktopSite(true, tabId) + components.core.store.dispatch(ContentAction.UpdateDesktopModeAction(tabId, true)) + // Reset preference value after opening the tab in desktop mode settings().openNextTabInDesktopMode = false } @@ -948,7 +898,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { webExtensionId = webExtensionState.id, webExtensionTitle = webExtensionState.name ) - navHost.navController.navigateBlockingForAsyncNavGraph(action) + navHost.navController.navigate(action) } /** @@ -976,14 +926,56 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { } } + private fun isFenixTheDefaultBrowser() { + // Launch this on a background thread so as not to affect startup performance + lifecycleScope.launch(IO) { + if ( + settings().checkIfFenixIsDefaultBrowserOnAppResume() + ) { + metrics.track(Event.ChangedToDefaultBrowser) + } + + DefaultBrowserNotificationWorker.setDefaultBrowserNotificationIfNeeded(applicationContext) + } + } + @VisibleForTesting internal fun isActivityColdStarted(startingIntent: Intent, activityIcicle: Bundle?): Boolean { // First time opening this activity in the task. // Cold start / start from Recents after back press. return activityIcicle == null && - // Activity was restarted from Recents after it was destroyed by Android while in background - // in cases of memory pressure / "Don't keep activities". - startingIntent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == 0 + // Activity was restarted from Recents after it was destroyed by Android while in background + // in cases of memory pressure / "Don't keep activities". + startingIntent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == 0 + } + + /** + * Indicates if the user should be redirected to the [BrowserFragment] or to the [HomeFragment], + * links from an external apps should always opened in the [BrowserFragment]. + */ + fun shouldStartOnHome(intent: Intent? = this.intent): Boolean { + if (!FeatureFlags.showStartOnHomeSettings) { + return false + } + return components.strictMode.resetAfter(StrictMode.allowThreadDiskReads()) { + // We only want to open on home when users tap the app, + // we want to ignore other cases when the app gets open by users clicking on links. + getSettings().shouldStartOnHome() && intent?.action == ACTION_MAIN + } + } + + @VisibleForTesting + internal fun getSettings(): Settings = settings() + + private fun shouldNavigateBrowserFragmentOnCouldStart(savedInstanceState: Bundle?): Boolean { + return isActivityColdStarted(intent, savedInstanceState) && + !externalSourceIntentProcessors.any { + it.process( + intent, + navHost.navController, + this.intent + ) + } } companion object { diff --git a/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt b/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt index 0c995371e..4556bc986 100644 --- a/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt @@ -6,10 +6,14 @@ package org.mozilla.fenix import android.app.Activity import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle import android.os.StrictMode import androidx.annotation.VisibleForTesting import mozilla.components.feature.intent.processing.IntentProcessor +import mozilla.components.support.utils.EXTRA_ACTIVITY_REFERRER_CATEGORY +import mozilla.components.support.utils.EXTRA_ACTIVITY_REFERRER_PACKAGE import org.mozilla.fenix.HomeActivity.Companion.PRIVATE_BROWSING_MODE import org.mozilla.fenix.components.IntentProcessorType import org.mozilla.fenix.components.getType @@ -54,6 +58,8 @@ class IntentReceiverActivity : Activity() { components.analytics.metrics.track(Event.OpenedLink(Event.OpenedLink.Mode.NORMAL)) } + addReferrerInformation(intent) + val processor = getIntentProcessors(private).firstOrNull { it.process(intent) } val intentProcessorType = components.intentProcessors.getType(processor) @@ -96,6 +102,28 @@ class IntentReceiverActivity : Activity() { modeDependentProcessors + NewTabShortcutIntentProcessor() } + + private fun addReferrerInformation(intent: Intent) { + // Pass along referrer information when possible. + // Referrer is supported for API>=22. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) { + return + } + // NB: referrer can be spoofed by the calling application. Use with caution. + val r = referrer ?: return + intent.putExtra(EXTRA_ACTIVITY_REFERRER_PACKAGE, r.host) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // Category is supported for API>=26. + r.host?.let { host -> + try { + val category = packageManager.getApplicationInfo(host, 0).category + intent.putExtra(EXTRA_ACTIVITY_REFERRER_CATEGORY, category) + } catch (e: PackageManager.NameNotFoundException) { + // At least we tried. + } + } + } + } } private fun Intent.stripUnwantedFlags() { diff --git a/app/src/main/java/org/mozilla/fenix/addons/AddonPermissionsDetailsFragment.kt b/app/src/main/java/org/mozilla/fenix/addons/AddonPermissionsDetailsFragment.kt index 53c04615a..c34974725 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/AddonPermissionsDetailsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/AddonPermissionsDetailsFragment.kt @@ -18,7 +18,8 @@ import org.mozilla.fenix.ext.showToolbar /** * A fragment to show the permissions of an add-on. */ -class AddonPermissionsDetailsFragment : Fragment(R.layout.fragment_add_on_permissions), +class AddonPermissionsDetailsFragment : + Fragment(R.layout.fragment_add_on_permissions), AddonPermissionsDetailsInteractor { private val args by navArgs() diff --git a/app/src/main/java/org/mozilla/fenix/addons/AddonPopupBaseFragment.kt b/app/src/main/java/org/mozilla/fenix/addons/AddonPopupBaseFragment.kt index 2bc24f97f..3cf7ce346 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/AddonPopupBaseFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/AddonPopupBaseFragment.kt @@ -10,10 +10,10 @@ import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import mozilla.components.browser.state.action.ContentAction import mozilla.components.browser.state.action.CustomTabListAction +import mozilla.components.browser.state.state.createCustomTab import mozilla.components.browser.state.state.CustomTabSessionState import mozilla.components.browser.state.state.EngineState import mozilla.components.browser.state.state.SessionState -import mozilla.components.browser.state.state.createCustomTab import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.prompt.PromptRequest import mozilla.components.concept.engine.window.WindowRequest @@ -33,6 +33,8 @@ abstract class AddonPopupBaseFragment : Fragment(), EngineSession.Observer, User protected var engineSession: EngineSession? = null private var canGoBack: Boolean = false + @Suppress("DEPRECATION") + // https://github.com/mozilla-mobile/fenix/issues/19920 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { session?.let { promptsFeature.set( @@ -43,7 +45,8 @@ abstract class AddonPopupBaseFragment : Fragment(), EngineSession.Observer, User fragmentManager = parentFragmentManager, onNeedToRequestPermissions = { permissions -> requestPermissions(permissions, REQUEST_CODE_PROMPT_PERMISSIONS) - }), + } + ), owner = this, view = view ) @@ -102,7 +105,10 @@ abstract class AddonPopupBaseFragment : Fragment(), EngineSession.Observer, User protected fun initializeSession(fromEngineSession: EngineSession? = null) { engineSession = fromEngineSession ?: requireComponents.core.engine.createSession() - session = createCustomTab("").copy(engineState = EngineState(engineSession)) + session = createCustomTab( + url = "", + source = SessionState.Source.Internal.CustomTab + ).copy(engineState = EngineState(engineSession)) requireComponents.core.store.dispatch(CustomTabListAction.AddCustomTabAction(session as CustomTabSessionState)) } 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 a98a767a9..6e42ffecc 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementFragment.kt @@ -119,7 +119,7 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management) addons?.forEach { addon -> val names = addon.translatableName names["en-US"]?.let { name -> - if (name.toLowerCase(Locale.ENGLISH).contains(addonNameSubStr.toLowerCase(Locale.ENGLISH))) { + if (name.lowercase().contains(addonNameSubStr.lowercase())) { searchedAddons.add(addon) } } diff --git a/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementView.kt b/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementView.kt index d1994c78a..4e9e24cab 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementView.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/AddonsManagementView.kt @@ -8,7 +8,6 @@ import androidx.navigation.NavController import mozilla.components.feature.addons.Addon import mozilla.components.feature.addons.ui.AddonsManagerAdapterDelegate import org.mozilla.fenix.R -import org.mozilla.fenix.ext.navigateBlockingForAsyncNavGraph import org.mozilla.fenix.ext.navigateSafe /** @@ -56,6 +55,6 @@ class AddonsManagementView( AddonsManagementFragmentDirections.actionAddonsManagementFragmentToNotYetSupportedAddonFragment( unsupportedAddons.toTypedArray() ) - navController.navigateBlockingForAsyncNavGraph(directions) + navController.navigate(directions) } } diff --git a/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt b/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt index cb550e2b5..bece38513 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt @@ -25,7 +25,6 @@ import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.navigateBlockingForAsyncNavGraph import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.ext.runIfFragmentIsAttached @@ -205,18 +204,14 @@ class InstalledAddonDetailsFragment : Fragment() { val shouldCreatePrivateSession = (activity as HomeActivity).browsingModeManager.mode.isPrivate - if (shouldCreatePrivateSession) { - components.useCases.tabsUseCases.addPrivateTab(settingUrl) - } else { - components.useCases.tabsUseCases.addTab(settingUrl) - } + components.useCases.tabsUseCases.addTab(settingUrl, private = shouldCreatePrivateSession) InstalledAddonDetailsFragmentDirections.actionGlobalBrowser(null) } else { InstalledAddonDetailsFragmentDirections .actionInstalledAddonFragmentToAddonInternalSettingsFragment(addon) } - Navigation.findNavController(this).navigateBlockingForAsyncNavGraph(directions) + Navigation.findNavController(this).navigate(directions) } } } @@ -227,7 +222,7 @@ class InstalledAddonDetailsFragment : Fragment() { InstalledAddonDetailsFragmentDirections.actionInstalledAddonFragmentToAddonDetailsFragment( addon ) - Navigation.findNavController(view).navigateBlockingForAsyncNavGraph(directions) + Navigation.findNavController(view).navigate(directions) } } @@ -237,7 +232,7 @@ class InstalledAddonDetailsFragment : Fragment() { InstalledAddonDetailsFragmentDirections.actionInstalledAddonFragmentToAddonPermissionsDetailsFragment( addon ) - Navigation.findNavController(view).navigateBlockingForAsyncNavGraph(directions) + Navigation.findNavController(view).navigate(directions) } } diff --git a/app/src/main/java/org/mozilla/fenix/autofill/AutofillSearchActivity.kt b/app/src/main/java/org/mozilla/fenix/autofill/AutofillSearchActivity.kt new file mode 100644 index 000000000..d72ef135b --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/autofill/AutofillSearchActivity.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.autofill + +import android.os.Build +import android.os.Bundle +import android.view.ViewGroup +import androidx.annotation.RequiresApi +import mozilla.components.feature.autofill.AutofillConfiguration +import mozilla.components.feature.autofill.ui.AbstractAutofillSearchActivity +import org.mozilla.fenix.ext.components + +/** + * Activity responsible for letting the user manually search and pick credentials for auto-filling a + * third-party app. + */ +@RequiresApi(Build.VERSION_CODES.O) +class AutofillSearchActivity : AbstractAutofillSearchActivity() { + override val configuration: AutofillConfiguration by lazy { components.autofillConfiguration } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // To avoid the dialog constantly resizing horizontally while typing, let's always use + // the full width of the screen for the dialog. + window.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } +} 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 6d6d4b7b2..18b03dd3f 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -77,7 +77,7 @@ import mozilla.components.feature.session.FullScreenFeature import mozilla.components.feature.session.PictureInPictureFeature import mozilla.components.feature.session.SessionFeature import mozilla.components.feature.session.SwipeRefreshFeature -import mozilla.components.feature.sitepermissions.SitePermissions +import mozilla.components.concept.engine.permission.SitePermissions import mozilla.components.feature.sitepermissions.SitePermissionsFeature import mozilla.components.lib.state.ext.consumeFlow import mozilla.components.lib.state.ext.flowScoped @@ -104,9 +104,7 @@ import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.toolbar.BrowserFragmentState import org.mozilla.fenix.components.toolbar.BrowserFragmentStore -import org.mozilla.fenix.components.toolbar.BrowserInteractor import org.mozilla.fenix.components.toolbar.BrowserToolbarView -import org.mozilla.fenix.components.toolbar.BrowserToolbarViewInteractor import org.mozilla.fenix.components.toolbar.DefaultBrowserToolbarController import org.mozilla.fenix.components.toolbar.DefaultBrowserToolbarMenuController import org.mozilla.fenix.components.toolbar.ToolbarIntegration @@ -133,10 +131,11 @@ import java.lang.ref.WeakReference import mozilla.components.feature.session.behavior.EngineViewBrowserToolbarBehavior import mozilla.components.feature.webauthn.WebAuthnFeature import mozilla.components.support.base.feature.ActivityResultHandler -import org.mozilla.fenix.ext.navigateBlockingForAsyncNavGraph import mozilla.components.support.ktx.android.view.enterToImmersiveMode import mozilla.components.support.ktx.kotlin.getOrigin import org.mozilla.fenix.GleanMetrics.PerfStartup +import org.mozilla.fenix.components.toolbar.interactor.BrowserToolbarInteractor +import org.mozilla.fenix.components.toolbar.interactor.DefaultBrowserToolbarInteractor import org.mozilla.fenix.ext.measureNoInline import org.mozilla.fenix.ext.secure import org.mozilla.fenix.settings.biometric.BiometricPromptFeature @@ -149,15 +148,19 @@ import mozilla.components.feature.session.behavior.ToolbarPosition as MozacToolb */ @ExperimentalCoroutinesApi @Suppress("TooManyFunctions", "LargeClass") -abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, ActivityResultHandler, - OnBackLongPressedListener, AccessibilityManager.AccessibilityStateChangeListener { +abstract class BaseBrowserFragment : + Fragment(), + UserInteractionHandler, + ActivityResultHandler, + OnBackLongPressedListener, + AccessibilityManager.AccessibilityStateChangeListener { private lateinit var browserFragmentStore: BrowserFragmentStore private lateinit var browserAnimator: BrowserAnimator - private var _browserInteractor: BrowserToolbarViewInteractor? = null - protected val browserInteractor: BrowserToolbarViewInteractor - get() = _browserInteractor!! + private var _browserToolbarInteractor: BrowserToolbarInteractor? = null + protected val browserToolbarInteractor: BrowserToolbarInteractor + get() = _browserToolbarInteractor!! @VisibleForTesting @Suppress("VariableNaming") @@ -236,25 +239,25 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit } final override fun onViewCreated(view: View, savedInstanceState: Bundle?) = - PerfStartup.baseBfragmentOnViewCreated.measureNoInline { // weird indentation to avoid breaking blame. - initializeUI(view) - - if (customTabSessionId == null) { - // We currently only need this observer to navigate to home - // in case all tabs have been removed on startup. No need to - // this if we have a known session to display. - observeRestoreComplete(requireComponents.core.store, findNavController()) - } + PerfStartup.baseBfragmentOnViewCreated.measureNoInline { // weird indentation to avoid breaking blame. + initializeUI(view) + + if (customTabSessionId == null) { + // We currently only need this observer to navigate to home + // in case all tabs have been removed on startup. No need to + // this if we have a known session to display. + observeRestoreComplete(requireComponents.core.store, findNavController()) + } - observeTabSelection(requireComponents.core.store) + observeTabSelection(requireComponents.core.store) - if (!onboarding.userHasBeenOnboarded()) { - observeTabSource(requireComponents.core.store) - } + if (!onboarding.userHasBeenOnboarded()) { + observeTabSource(requireComponents.core.store) + } - requireContext().accessibilityManager.addAccessibilityStateChangeListener(this) - Unit - } + requireContext().accessibilityManager.addAccessibilityStateChangeListener(this) + Unit + } private fun initializeUI(view: View) { val tab = getCurrentTab() @@ -266,7 +269,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit } } - @Suppress("ComplexMethod", "LongMethod") + @Suppress("ComplexMethod", "LongMethod", "DEPRECATION") + // https://github.com/mozilla-mobile/fenix/issues/19920 @CallSuper internal open fun initializeUI(view: View, tab: SessionState) { val context = requireContext() @@ -309,7 +313,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit thumbnailsFeature.get()?.requestScreenshot() findNavController().nav( R.id.browserFragment, - getTrayDirection(context) + BrowserFragmentDirections.actionGlobalTabsTrayFragment() ) }, onCloseTab = { closedSession -> @@ -357,7 +361,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit browserStore = store ) - _browserInteractor = BrowserInteractor( + _browserToolbarInteractor = DefaultBrowserToolbarInteractor( browserToolbarController, browserToolbarMenuController ) @@ -365,7 +369,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit _browserToolbarView = BrowserToolbarView( container = view.browserLayout, toolbarPosition = context.settings().toolbarPosition, - interactor = browserInteractor, + interactor = browserToolbarInteractor, customTabSession = customTabSessionId?.let { store.state.findCustomTab(it) }, lifecycleOwner = viewLifecycleOwner ) @@ -420,7 +424,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit window = requireActivity().window, store = store, customTabId = customTabSessionId, - isSecure = { !allowScreenshotsInPrivateMode && it.content.private } + isSecure = { !allowScreenshotsInPrivateMode && it.content.private }, + clearFlagOnStop = false ), owner = this, view = view @@ -489,16 +494,14 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit val dynamicDownloadDialog = DynamicDownloadDialog( container = view.browserLayout, downloadState = downloadState, - metrics = requireComponents.analytics.metrics, didFail = downloadJobStatus == DownloadState.Status.FAILED, tryAgain = downloadFeature::tryAgain, onCannotOpenFile = { showCannotOpenFileError(view.browserLayout, context, it) }, view = view.viewDynamicDownloadDialog, - toolbarHeight = toolbarHeight, - onDismiss = { sharedViewModel.downloadDialogState.remove(downloadState.sessionId) } - ) + toolbarHeight = toolbarHeight + ) { sharedViewModel.downloadDialogState.remove(downloadState.sessionId) } dynamicDownloadDialog.show() browserToolbarView.expand() @@ -585,7 +588,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit showPage = true, sessionId = getCurrentTab()?.id ) - findNavController().navigateBlockingForAsyncNavGraph(directions) + findNavController().navigate(directions) } }, onNeedToRequestPermissions = { permissions -> @@ -596,14 +599,14 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit browserAnimator.captureEngineViewAndDrawStatically { val directions = NavGraphDirections.actionGlobalSavedLoginsAuthFragment() - findNavController().navigateBlockingForAsyncNavGraph(directions) + findNavController().navigate(directions) } }, creditCardPickerView = creditCardSelectBar, onManageCreditCards = { val directions = NavGraphDirections.actionGlobalCreditCardsSettingFragment() - findNavController().navigateBlockingForAsyncNavGraph(directions) + findNavController().navigate(directions) }, onSelectCreditCard = { showBiometricPrompt(context) @@ -650,7 +653,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit sitePermissionsFeature.set( feature = SitePermissionsFeature( context = context, - storage = context.components.core.permissionStorage.permissionsStorage, + storage = context.components.core.geckoSitePermissionsStorage, fragmentManager = parentFragmentManager, promptsStyling = SitePermissionsFeature.PromptsStyling( gravity = getAppropriateLayoutGravity(), @@ -822,15 +825,17 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit internal fun expandToolbarOnNavigation(store: BrowserStore) { consumeFlow(store) { flow -> flow.mapNotNull { - state -> state.findCustomTabOrSelectedTab(customTabSessionId) - } - .ifAnyChanged { - tab -> arrayOf(tab.content.url, tab.content.loadRequest) - } - .collect { - findInPageIntegration.onBackPressed() - browserToolbarView.expand() + state -> + state.findCustomTabOrSelectedTab(customTabSessionId) } + .ifAnyChanged { + tab -> + arrayOf(tab.content.url, tab.content.loadRequest) + } + .collect { + findInPageIntegration.onBackPressed() + browserToolbarView.expand() + } } } @@ -889,7 +894,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit DynamicDownloadDialog( container = view.browserLayout, downloadState = savedDownloadState.first, - metrics = requireComponents.analytics.metrics, didFail = savedDownloadState.second, tryAgain = onTryAgain, onCannotOpenFile = { @@ -906,8 +910,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit @VisibleForTesting internal fun shouldPullToRefreshBeEnabled(inFullScreen: Boolean): Boolean { return FeatureFlags.pullToRefreshEnabled && - requireContext().settings().isPullToRefreshEnabledInBrowser && - !inFullScreen + requireContext().settings().isPullToRefreshEnabledInBrowser && + !inFullScreen } @VisibleForTesting @@ -980,12 +984,12 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit flow.ifChanged { it.selectedTabId } - .mapNotNull { - it.selectedTab - } - .collect { - handleTabSelected(it) - } + .mapNotNull { + it.selectedTab + } + .collect { + handleTabSelected(it) + } } } @@ -997,14 +1001,14 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit state.selectedTab } .collect { - if (!onboarding.userHasBeenOnboarded() && - it.content.loadRequest?.triggeredByRedirect != true && - it.source !in intentSourcesList && - it.content.url !in onboardingLinksList - ) { - onboarding.finish() + if (!onboarding.userHasBeenOnboarded() && + it.content.loadRequest?.triggeredByRedirect != true && + it.source !is SessionState.Source.External && + it.content.url !in onboardingLinksList + ) { + onboarding.finish() + } } - } } } @@ -1069,14 +1073,14 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit @CallSuper override fun onBackPressed(): Boolean { return findInPageIntegration.onBackPressed() || - fullScreenFeature.onBackPressed() || - promptsFeature.onBackPressed() || - sessionFeature.onBackPressed() || - removeSessionIfNeeded() + fullScreenFeature.onBackPressed() || + promptsFeature.onBackPressed() || + sessionFeature.onBackPressed() || + removeSessionIfNeeded() } override fun onBackLongPressed(): Boolean { - findNavController().navigateBlockingForAsyncNavGraph( + findNavController().navigate( NavGraphDirections.actionGlobalTabHistoryDialogFragment( activeSessionId = customTabSessionId ) @@ -1137,7 +1141,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit */ protected open fun removeSessionIfNeeded(): Boolean { getCurrentTab()?.let { session -> - return if (session.source == SessionState.Source.ACTION_VIEW) { + return if (session.source is SessionState.Source.External && !session.restored) { activity?.finish() requireComponents.useCases.tabsUseCases.removeTab(session.id) true @@ -1186,7 +1190,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit val tab = getCurrentTab() ?: return viewLifecycleOwner.lifecycleScope.launch(Main) { val sitePermissions: SitePermissions? = tab.content.url.getOrigin()?.let { origin -> - val storage = requireComponents.core.permissionStorage + val storage = requireComponents.core.permissionStorage storage.findSitePermissionsBy(origin) } @@ -1343,7 +1347,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit requireContext().accessibilityManager.removeAccessibilityStateChangeListener(this) _browserToolbarView = null - _browserInteractor = null + _browserToolbarInteractor = null } override fun onAttach(context: Context) { @@ -1379,17 +1383,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit .show() } - /** - * Retrieves the correct tray direction while using a feature flag. - * - * Remove this when [FeatureFlags.tabsTrayRewrite] is removed. - */ - private fun getTrayDirection(context: Context) = if (context.settings().tabsTrayRewrite) { - BrowserFragmentDirections.actionGlobalTabsTrayFragment() - } else { - BrowserFragmentDirections.actionGlobalTabTrayDialogFragment() - } - companion object { private const val KEY_CUSTOM_TAB_SESSION_ID = "custom_tab_session_id" private const val REQUEST_CODE_DOWNLOAD_PERMISSIONS = 1 @@ -1400,12 +1393,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE), SupportUtils.getFirefoxAccountSumoUrl() ) - - val intentSourcesList: List = listOf( - SessionState.Source.ACTION_SEARCH, - SessionState.Source.ACTION_SEND, - SessionState.Source.ACTION_VIEW - ) } override fun onAccessibilityStateChanged(enabled: Boolean) { 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 ac70ff92c..d0e985cfc 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -8,6 +8,7 @@ import android.content.Context import android.os.StrictMode import android.view.View import android.view.ViewGroup +import androidx.annotation.VisibleForTesting import androidx.appcompat.content.res.AppCompatResources import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController @@ -16,29 +17,31 @@ import kotlinx.android.synthetic.main.fragment_browser.* import kotlinx.android.synthetic.main.fragment_browser.view.* import kotlinx.coroutines.ExperimentalCoroutinesApi import mozilla.components.browser.state.selector.findTab -import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.state.SessionState +import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.thumbnails.BrowserThumbnails import mozilla.components.browser.toolbar.BrowserToolbar +import mozilla.components.concept.engine.permission.SitePermissions import mozilla.components.feature.app.links.AppLinksUseCases import mozilla.components.feature.contextmenu.ContextMenuCandidate import mozilla.components.feature.readerview.ReaderViewFeature -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.components.FenixSnackbar import org.mozilla.fenix.components.TabCollectionStorage import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.navigateBlockingForAsyncNavGraph import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.navigateSafe import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.ext.runIfFragmentIsAttached import org.mozilla.fenix.shortcut.PwaOnboardingObserver +import org.mozilla.fenix.theme.ThemeManager import org.mozilla.fenix.trackingprotection.TrackingProtectionOverlay /** @@ -50,7 +53,8 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { private val windowFeature = ViewBoundFeatureWrapper() private val openInAppOnboardingObserver = ViewBoundFeatureWrapper() - private val trackingProtectionOverlayObserver = ViewBoundFeatureWrapper() + private val trackingProtectionOverlayObserver = + ViewBoundFeatureWrapper() private var readerModeAvailable = false private var pwaOnboardingObserver: PwaOnboardingObserver? = null @@ -75,20 +79,40 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { ) } + if (FeatureFlags.showHomeButtonFeature) { + val homeAction = BrowserToolbar.Button( + imageDrawable = AppCompatResources.getDrawable( + requireContext(), + R.drawable.mozac_ic_home + )!!, + contentDescription = requireContext().getString(R.string.browser_toolbar_home), + iconTintColorResource = ThemeManager.resolveAttribute(R.attr.primaryText, context), + listener = browserToolbarInteractor::onHomeButtonClicked + ) + + browserToolbarView.view.addNavigationAction(homeAction) + } + val readerModeAction = BrowserToolbar.ToggleButton( - image = AppCompatResources.getDrawable(requireContext(), R.drawable.ic_readermode)!!, + image = AppCompatResources.getDrawable( + requireContext(), + R.drawable.ic_readermode + )!!, imageSelected = - AppCompatResources.getDrawable(requireContext(), R.drawable.ic_readermode_selected)!!, + AppCompatResources.getDrawable( + requireContext(), + R.drawable.ic_readermode_selected + )!!, contentDescription = requireContext().getString(R.string.browser_menu_read), contentDescriptionSelected = requireContext().getString(R.string.browser_menu_read_close), visible = { readerModeAvailable }, selected = getCurrentTab()?.let { - activity?.components?.core?.store?.state?.findTab(it.id)?.readerState?.active - } ?: false, - listener = browserInteractor::onReaderModePressed + activity?.components?.core?.store?.state?.findTab(it.id)?.readerState?.active + } ?: false, + listener = browserToolbarInteractor::onReaderModePressed ) browserToolbarView.view.addPageAction(readerModeAction) @@ -183,10 +207,21 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { override fun onStop() { super.onStop() - + updateLastBrowseActivity() + if (requireContext().settings().historyMetadataFeature) { + updateHistoryMetadata() + } pwaOnboardingObserver?.stop() } + private fun updateHistoryMetadata() { + getCurrentTab()?.let { tab -> + (tab as? TabSessionState)?.historyMetadata?.let { + requireComponents.core.historyMetadataService.updateMetadata(it, tab) + } + } + } + private fun subscribeToTabCollections() { Observer> { requireComponents.core.tabCollectionStorage.cachedTabCollections = it @@ -222,22 +257,27 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { override fun navToTrackingProtectionPanel(tab: SessionState) { val navController = findNavController() - requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains -> - val isEnabled = tab.trackingProtection.enabled && !contains - val directions = - BrowserFragmentDirections.actionBrowserFragmentToTrackingProtectionPanelDialogFragment( - sessionId = tab.id, - url = tab.content.url, - trackingProtectionEnabled = isEnabled, - gravity = getAppropriateLayoutGravity() - ) - navController.navigateSafe(R.id.browserFragment, directions) + runIfFragmentIsAttached { + val isEnabled = tab.trackingProtection.enabled && !contains + val directions = + BrowserFragmentDirections.actionBrowserFragmentToTrackingProtectionPanelDialogFragment( + sessionId = tab.id, + url = tab.content.url, + trackingProtectionEnabled = isEnabled, + gravity = getAppropriateLayoutGravity() + ) + navController.navigateSafe(R.id.browserFragment, directions) + } } } private val collectionStorageObserver = object : TabCollectionStorage.Observer { - override fun onCollectionCreated(title: String, sessions: List, id: Long?) { + override fun onCollectionCreated( + title: String, + sessions: List, + id: Long? + ) { showTabSavedToCollectionSnackbar(sessions.size, true) } @@ -245,7 +285,10 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { showTabSavedToCollectionSnackbar(sessions.size) } - private fun showTabSavedToCollectionSnackbar(tabSize: Int, isNewCollection: Boolean = false) { + private fun showTabSavedToCollectionSnackbar( + tabSize: Int, + isNewCollection: Boolean = false + ) { view?.let { view -> val messageStringRes = when { isNewCollection -> { @@ -265,7 +308,7 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { ) .setText(view.context.getString(messageStringRes)) .setAction(requireContext().getString(R.string.create_collection_view)) { - findNavController().navigateBlockingForAsyncNavGraph( + findNavController().navigate( BrowserFragmentDirections.actionGlobalHome(focusOnAddressBar = false) ) } @@ -289,7 +332,19 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { context.components.useCases.contextMenuUseCases, view, FenixSnackbarDelegate(view) - ) + ContextMenuCandidate.createOpenInExternalAppCandidate(requireContext(), - contextMenuCandidateAppLinksUseCases) + ) + ContextMenuCandidate.createOpenInExternalAppCandidate( + requireContext(), + contextMenuCandidateAppLinksUseCases + ) + } + + /** + * Updates the last time the user was active on the [BrowserFragment]. + * This is useful to determine if the user has to start on the [HomeFragment] + * or it should go directly to the [BrowserFragment]. + */ + @VisibleForTesting + internal fun updateLastBrowseActivity() { + requireContext().settings().lastBrowseActivity = System.currentTimeMillis() } } diff --git a/app/src/main/java/org/mozilla/fenix/browser/FenixSnackbarDelegate.kt b/app/src/main/java/org/mozilla/fenix/browser/FenixSnackbarDelegate.kt index 6d2a4cdfc..5de15a759 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/FenixSnackbarDelegate.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/FenixSnackbarDelegate.kt @@ -28,7 +28,8 @@ class FenixSnackbarDelegate(private val view: View) : ContextMenuCandidate.Snack .setAction(view.context.getString(action)) { listener.invoke(view) } .show() } else { - FenixSnackbar.make(view, + FenixSnackbar.make( + view, duration = FenixSnackbar.LENGTH_SHORT, isDisplayedWithBrowserToolbar = true ) diff --git a/app/src/main/java/org/mozilla/fenix/browser/OpenInAppOnboardingObserver.kt b/app/src/main/java/org/mozilla/fenix/browser/OpenInAppOnboardingObserver.kt index 9205a26bd..692d02289 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/OpenInAppOnboardingObserver.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/OpenInAppOnboardingObserver.kt @@ -24,9 +24,6 @@ import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged import org.mozilla.fenix.R import org.mozilla.fenix.browser.infobanner.DynamicInfoBanner import org.mozilla.fenix.browser.infobanner.InfoBanner -import org.mozilla.fenix.components.metrics.Event -import org.mozilla.fenix.components.metrics.Event.BannerOpenInAppGoToSettings -import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.nav import org.mozilla.fenix.utils.Settings @@ -58,22 +55,23 @@ class OpenInAppOnboardingObserver( flow.mapNotNull { state -> state.selectedTab } - .ifAnyChanged { - tab -> arrayOf(tab.content.url, tab.content.loading) - } - .collect { tab -> - if (tab.content.url != currentUrl) { - sessionDomainForDisplayedBanner?.let { - if (tab.content.url.tryGetHostFromUrl() != it) { - infoBanner?.dismiss() + .ifAnyChanged { + tab -> + arrayOf(tab.content.url, tab.content.loading) + } + .collect { tab -> + if (tab.content.url != currentUrl) { + sessionDomainForDisplayedBanner?.let { + if (tab.content.url.tryGetHostFromUrl() != it) { + infoBanner?.dismiss() + } } + currentUrl = tab.content.url + } else { + // Loading state has changed + maybeShowOpenInAppBanner(tab.content.url, tab.content.loading) } - currentUrl = tab.content.url - } else { - // Loading state has changed - maybeShowOpenInAppBanner(tab.content.url, tab.content.loading) } - } } } @@ -92,7 +90,6 @@ class OpenInAppOnboardingObserver( infoBanner?.showBanner() sessionDomainForDisplayedBanner = url.tryGetHostFromUrl() settings.shouldShowOpenInAppBanner = false - context.components.analytics.metrics.track(Event.BannerOpenInAppDisplayed) } } @@ -104,18 +101,12 @@ class OpenInAppOnboardingObserver( dismissText = context.getString(R.string.open_in_app_cfr_negative_button_text), actionText = context.getString(R.string.open_in_app_cfr_positive_button_text), container = container, - shouldScrollWithTopToolbar = shouldScrollWithTopToolbar, - dismissAction = ::dismissAction + shouldScrollWithTopToolbar = shouldScrollWithTopToolbar ) { val directions = BrowserFragmentDirections.actionBrowserFragmentToSettingsFragment( preferenceToScrollTo = context.getString(R.string.pref_key_open_links_in_external_app) ) - context.components.analytics.metrics.track(BannerOpenInAppGoToSettings) navController.nav(R.id.browserFragment, directions) } } - - private fun dismissAction() { - context.components.analytics.metrics.track(Event.BannerOpenInAppDismissed) - } } 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 d6284bc4b..4c69cbf73 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt @@ -214,8 +214,10 @@ class ToolbarGestureHandler( val reverseFling = abs(velocityX) >= minimumFlingVelocity && !velocityMatchesDirection - return !reverseFling && (previewWidth / windowWidth >= GESTURE_FINISH_PERCENT || - abs(velocityX) >= minimumFlingVelocity) + return !reverseFling && ( + previewWidth / windowWidth >= GESTURE_FINISH_PERCENT || + abs(velocityX) >= minimumFlingVelocity + ) } private fun getAnimator(finalContextX: Float, duration: Long): ValueAnimator { @@ -274,6 +276,8 @@ class ToolbarGestureHandler( }.start() } + @Suppress("DEPRECATION") + // https://github.com/mozilla-mobile/fenix/issues/19929 private fun PointF.isInToolbar(): Boolean { val toolbarLocation = toolbarLayout.getRectWithScreenLocation() // In Android 10, the system gesture touch area overlaps the bottom of the toolbar, so diff --git a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationView.kt b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationView.kt index 7b23249a9..34db0f881 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationView.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationView.kt @@ -170,9 +170,12 @@ class CollectionCreationView( setOnClickListener { name_collection_edittext.hideKeyboard() val handler = Handler(Looper.getMainLooper()) - handler.postDelayed({ - interactor.onBackPressed(SaveCollectionStep.NameCollection) - }, TRANSITION_DURATION) + handler.postDelayed( + { + interactor.onBackPressed(SaveCollectionStep.NameCollection) + }, + TRANSITION_DURATION + ) } } @@ -216,9 +219,12 @@ class CollectionCreationView( setOnClickListener { name_collection_edittext.hideKeyboard() val handler = Handler(Looper.getMainLooper()) - handler.postDelayed({ - interactor.onBackPressed(SaveCollectionStep.RenameCollection) - }, TRANSITION_DURATION) + handler.postDelayed( + { + interactor.onBackPressed(SaveCollectionStep.RenameCollection) + }, + TRANSITION_DURATION + ) } } transition.addListener(object : Transition.TransitionListener { diff --git a/app/src/main/java/org/mozilla/fenix/collections/CollectionsDialog.kt b/app/src/main/java/org/mozilla/fenix/collections/CollectionsDialog.kt index a5c6a8ba8..dae325299 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CollectionsDialog.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CollectionsDialog.kt @@ -22,12 +22,10 @@ import org.mozilla.fenix.ext.getDefaultCollectionNumber /** * A lambda that is invoked when a confirmation button in a [CollectionsDialog] is clicked. * - * A [TabCollection] of the selected collected is passed to the delegate when confirmed. If null, - * then a new collection is created. - * - * A list of [TabSessionState] is returned that will be put into the collections storage. + * The [TabCollection] ID of the selected collection is passed to the delegate when confirmed. + * If the selected collection was newly created then [Boolean] is set to true. */ -typealias OnPositiveButtonClick = (collection: TabCollection?) -> List +typealias OnPositiveButtonClick = (id: Long?, isNewCollection: Boolean) -> Unit /** * A lambda that is invoked when a cancel button in a [CollectionsDialog] is clicked. @@ -42,6 +40,7 @@ typealias OnNegativeButtonClick = () -> Unit */ data class CollectionsDialog( val storage: TabCollectionStorage, + val sessionList: List, val onPositiveButtonClick: OnPositiveButtonClick, val onNegativeButtonClick: OnNegativeButtonClick ) @@ -67,10 +66,10 @@ fun CollectionsDialog.show( val selectedCollection = (list.adapter as CollectionsListAdapter).getSelectedCollection() val collection = storage.cachedTabCollections[selectedCollection] - val sessionList = onPositiveButtonClick.invoke(collection) MainScope().launch { - storage.addTabsToCollection(collection, sessionList) + val id = storage.addTabsToCollection(collection, sessionList) + onPositiveButtonClick.invoke(id, false) } dialog.dismiss() @@ -112,13 +111,13 @@ internal fun CollectionsDialog.showAddNewDialog( AlertDialog.Builder(context) .setTitle(R.string.tab_tray_add_new_collection) .setView(layout).setPositiveButton(android.R.string.ok) { dialog, _ -> - val sessionList = onPositiveButtonClick.invoke(null) MainScope().launch { - storage.createCollection( + val id = storage.createCollection( collectionNameEditText.text.toString(), sessionList ) + onPositiveButtonClick.invoke(id, true) } dialog.dismiss() diff --git a/app/src/main/java/org/mozilla/fenix/components/Analytics.kt b/app/src/main/java/org/mozilla/fenix/components/Analytics.kt index d875dd63d..af365fd00 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Analytics.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Analytics.kt @@ -14,10 +14,8 @@ import mozilla.components.lib.crash.service.GleanCrashReporterService import mozilla.components.lib.crash.service.MozillaSocorroService import mozilla.components.lib.crash.service.SentryService import mozilla.components.service.nimbus.NimbusApi -import mozilla.components.service.nimbus.NimbusDisabled import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.Config -import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.ReleaseChannel @@ -25,7 +23,6 @@ import org.mozilla.fenix.components.metrics.AdjustMetricsService import org.mozilla.fenix.components.metrics.GleanMetricsService import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.experiments.createNimbus -import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.perf.lazyMonitored import org.mozilla.fenix.utils.Mockable @@ -59,9 +56,11 @@ class Analytics( // The name "Fenix" here matches the product name on Socorro and is unrelated to the actual app name: // https://bugzilla.mozilla.org/show_bug.cgi?id=1523284 - val socorroService = MozillaSocorroService(context, appName = "Fenix", + val socorroService = MozillaSocorroService( + context, appName = "Fenix", version = MOZ_APP_VERSION, buildId = MOZ_APP_BUILDID, vendor = MOZ_APP_VENDOR, - releaseChannel = MOZ_UPDATE_CHANNEL) + releaseChannel = MOZ_UPDATE_CHANNEL + ) services.add(socorroService) val intent = Intent(context, HomeActivity::class.java).apply { @@ -102,11 +101,7 @@ class Analytics( } val experiments: NimbusApi by lazyMonitored { - if (FeatureFlags.nimbusExperiments) { - createNimbus(context, BuildConfig.NIMBUS_ENDPOINT) - } else { - NimbusDisabled() - } + createNimbus(context, BuildConfig.NIMBUS_ENDPOINT) } } 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 c0073fff9..ce71bc221 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Components.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Components.kt @@ -7,6 +7,8 @@ package org.mozilla.fenix.components import android.app.Application import android.content.Context import android.content.Intent +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext import androidx.core.net.toUri import com.google.android.play.core.review.ReviewManagerFactory import mozilla.components.feature.addons.AddonManager @@ -15,7 +17,6 @@ import mozilla.components.feature.addons.migration.SupportedAddonsChecker import mozilla.components.feature.addons.update.AddonUpdater import mozilla.components.feature.addons.update.DefaultAddonUpdater import mozilla.components.feature.autofill.AutofillConfiguration -import mozilla.components.feature.sitepermissions.SitePermissionsStorage import mozilla.components.lib.publicsuffixlist.PublicSuffixList import mozilla.components.support.migration.state.MigrationStore import io.github.forkmaintainers.iceraven.components.PagedAddonCollectionProvider @@ -24,8 +25,8 @@ import org.mozilla.fenix.Config import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.autofill.AutofillConfirmActivity +import org.mozilla.fenix.autofill.AutofillSearchActivity import org.mozilla.fenix.autofill.AutofillUnlockActivity -import org.mozilla.fenix.components.metrics.AppStartupTelemetry import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.perf.AppStartReasonProvider @@ -70,7 +71,6 @@ class Components(private val context: Context) { UseCases( context, core.engine, - core.sessionManager, core.store, core.webAppShortcutManager, core.topSitesStorage, @@ -116,8 +116,6 @@ class Components(private val context: Context) { } } - val appStartupTelemetry by lazyMonitored { AppStartupTelemetry(analytics.metrics) } - @Suppress("MagicNumber") val addonUpdater by lazyMonitored { DefaultAddonUpdater(context, AddonUpdater.Frequency(12, TimeUnit.HOURS)) @@ -125,7 +123,8 @@ class Components(private val context: Context) { @Suppress("MagicNumber") val supportedAddonsChecker by lazyMonitored { - DefaultSupportedAddonsChecker(context, SupportedAddonsChecker.Frequency(12, TimeUnit.HOURS), + DefaultSupportedAddonsChecker( + context, SupportedAddonsChecker.Frequency(12, TimeUnit.HOURS), onNotificationClickIntent = Intent(context, HomeActivity::class.java).apply { action = Intent.ACTION_VIEW flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK @@ -147,10 +146,6 @@ class Components(private val context: Context) { addonCollectionProvider.setCollectionName(addonsCollection) } - val sitePermissionsStorage by lazyMonitored { - SitePermissionsStorage(context, context.components.core.engine) - } - val analytics by lazyMonitored { Analytics(context) } val publicSuffixList by lazyMonitored { PublicSuffixList(context) } val clipboardHandler by lazyMonitored { ClipboardHandler(context) } @@ -175,6 +170,7 @@ class Components(private val context: Context) { publicSuffixList = publicSuffixList, unlockActivity = AutofillUnlockActivity::class.java, confirmActivity = AutofillConfirmActivity::class.java, + searchActivity = AutofillSearchActivity::class.java, applicationName = context.getString(R.string.app_name), httpClient = core.client ) @@ -184,3 +180,10 @@ class Components(private val context: Context) { val startupActivityLog by lazyMonitored { StartupActivityLog() } val startupStateProvider by lazyMonitored { StartupStateProvider(startupActivityLog, appStartReasonProvider) } } + +/** + * Returns the [Components] object from within a [Composable]. + */ +val components: Components + @Composable + get() = LocalContext.current.components 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 c18624e34..e39c04bad 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Core.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Core.kt @@ -4,20 +4,17 @@ package org.mozilla.fenix.components -import org.mozilla.fenix.gecko.GeckoProvider import android.content.Context import android.content.res.Configuration import android.os.Build import android.os.StrictMode import androidx.core.content.ContextCompat -import io.sentry.Sentry import mozilla.components.browser.engine.gecko.GeckoEngine import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient +import mozilla.components.browser.engine.gecko.permission.GeckoSitePermissionsStorage 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.engine.EngineMiddleware import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.browser.storage.sync.PlacesBookmarksStorage @@ -34,17 +31,22 @@ import mozilla.components.feature.customtabs.store.CustomTabsServiceStore import mozilla.components.feature.downloads.DownloadMiddleware import mozilla.components.feature.logins.exceptions.LoginExceptionStorage import mozilla.components.feature.media.MediaSessionFeature +import mozilla.components.feature.media.middleware.LastMediaAccessMiddleware import mozilla.components.feature.media.middleware.RecordingDevicesMiddleware import mozilla.components.feature.prompts.PromptMiddleware import mozilla.components.feature.pwa.ManifestStorage import mozilla.components.feature.pwa.WebAppShortcutManager import mozilla.components.feature.readerview.ReaderViewMiddleware import mozilla.components.feature.recentlyclosed.RecentlyClosedMiddleware +import mozilla.components.feature.search.middleware.AdsTelemetryMiddleware import mozilla.components.feature.search.middleware.SearchMiddleware import mozilla.components.feature.search.region.RegionMiddleware +import mozilla.components.feature.search.telemetry.ads.AdsTelemetry +import mozilla.components.feature.search.telemetry.incontent.InContentTelemetry import mozilla.components.feature.session.HistoryDelegate import mozilla.components.feature.session.middleware.LastAccessMiddleware import mozilla.components.feature.session.middleware.undo.UndoMiddleware +import mozilla.components.feature.sitepermissions.OnDiskSitePermissionsStorage import mozilla.components.feature.top.sites.DefaultTopSitesStorage import mozilla.components.feature.top.sites.PinnedSiteStorage import mozilla.components.feature.webcompat.WebCompatFeature @@ -69,16 +71,19 @@ import org.mozilla.fenix.components.search.SearchMigration import org.mozilla.fenix.downloads.DownloadService import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.gecko.GeckoProvider +import org.mozilla.fenix.historymetadata.DefaultHistoryMetadataService +import org.mozilla.fenix.historymetadata.HistoryMetadataMiddleware +import org.mozilla.fenix.historymetadata.HistoryMetadataService import org.mozilla.fenix.media.MediaSessionService import org.mozilla.fenix.perf.StrictModeManager import org.mozilla.fenix.perf.lazyMonitored -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.telemetry.TelemetryMiddleware import org.mozilla.fenix.utils.Mockable import org.mozilla.fenix.utils.getUndoDelay +import org.mozilla.geckoview.GeckoRuntime /** * Component group for all core browser functionality. @@ -98,7 +103,7 @@ class Core( val defaultSettings = DefaultSettings( requestInterceptor = requestInterceptor, remoteDebuggingEnabled = context.settings().isRemoteDebuggingEnabled && - Build.VERSION.SDK_INT >= Build.VERSION_CODES.M, + Build.VERSION.SDK_INT >= Build.VERSION_CODES.M, testingModeEnabled = false, trackingProtectionPolicy = trackingProtectionPolicyFactory.createTrackingProtectionPolicy(), historyTrackingDelegate = HistoryDelegate(lazyHistoryStorage), @@ -118,12 +123,7 @@ class Core( GeckoEngine( context, defaultSettings, - GeckoProvider.getOrCreateRuntime( - context, - lazyAutofillStorage, - lazyPasswordsStorage, - trackingProtectionPolicyFactory.createTrackingProtectionPolicy() - ) + geckoRuntime ).also { WebCompatFeature.install(it) @@ -154,15 +154,23 @@ class Core( val client: Client by lazyMonitored { GeckoViewFetchClient( context, - GeckoProvider.getOrCreateRuntime( - context, - lazyAutofillStorage, - lazyPasswordsStorage, - trackingProtectionPolicyFactory.createTrackingProtectionPolicy() - ) + geckoRuntime + ) + } + + val geckoRuntime: GeckoRuntime by lazyMonitored { + GeckoProvider.getOrCreateRuntime( + context, + lazyAutofillStorage, + lazyPasswordsStorage, + trackingProtectionPolicyFactory.createTrackingProtectionPolicy() ) } + val geckoSitePermissionsStorage by lazyMonitored { + GeckoSitePermissionsStorage(geckoRuntime, OnDiskSitePermissionsStorage(context)) + } + val sessionStorage: SessionStorage by lazyMonitored { SessionStorage(context, engine = engine) } @@ -187,11 +195,10 @@ class Core( ReaderViewMiddleware(), TelemetryMiddleware( context.settings(), - adsTelemetry, metrics ), ThumbnailsMiddleware(thumbnailStorage), - UndoMiddleware(::lookupSessionManager, context.getUndoDelay()), + UndoMiddleware(context.getUndoDelay()), RegionMiddleware(context, locationService), SearchMiddleware( context, @@ -199,22 +206,34 @@ class Core( migration = SearchMigration(context) ), RecordingDevicesMiddleware(context), - PromptMiddleware() + PromptMiddleware(), + AdsTelemetryMiddleware(adsTelemetry), + LastMediaAccessMiddleware() ) + if (context.settings().historyMetadataFeature) { + middlewareList += HistoryMetadataMiddleware(historyMetadataService) + } + BrowserStore( - middleware = middlewareList + EngineMiddleware.create(engine, ::findSessionById) - ) - } + middleware = middlewareList + EngineMiddleware.create(engine) + ).apply { + // Install the "icons" WebExtension to automatically load icons for every visited website. + icons.install(engine, this) - @Suppress("Deprecation") - private fun lookupSessionManager(): SessionManager { - return sessionManager - } + // Install the "ads" WebExtension to get the links in an partner page. + adsTelemetry.install(engine, this) + + // Install the "cookies" WebExtension and tracks user interaction with SERPs. + searchTelemetry.install(engine, this) + + WebNotificationFeature( + context, engine, icons, R.drawable.ic_status_logo, + permissionStorage.permissionsStorage, HomeActivity::class.java + ) - @Suppress("Deprecation") - private fun findSessionById(tabId: String): Session? { - return sessionManager.findSessionById(tabId) + MediaSessionFeature(context, MediaSessionService::class.java, this).start() + } } /** @@ -230,29 +249,11 @@ class Core( } /** - * The session manager component provides access to a centralized registry of - * all browser sessions (i.e. tabs). It is initialized here to persist and restore - * sessions from the [SessionStorage], and with a default session (about:blank) in - * case all sessions/tabs are closed. + * The [HistoryMetadataService] is used to record history metadata. */ - @Deprecated("Use browser store (for reading) and use cases (for writing) instead") - val sessionManager by lazyMonitored { - SessionManager(engine, store).also { - // Install the "icons" WebExtension to automatically load icons for every visited website. - icons.install(engine, store) - - // Install the "ads" WebExtension to get the links in an partner page. - adsTelemetry.install(engine, store) - - // Install the "cookies" WebExtension and tracks user interaction with SERPs. - searchTelemetry.install(engine, store) - - WebNotificationFeature( - context, engine, icons, R.drawable.ic_status_logo, - permissionStorage.permissionsStorage, HomeActivity::class.java - ) - - MediaSessionFeature(context, MediaSessionService::class.java, store).start() + val historyMetadataService: HistoryMetadataService by lazyMonitored { + DefaultHistoryMetadataService(storage = historyStorage).apply { + cleanup(System.currentTimeMillis() - HISTORY_METADATA_MAX_AGE_IN_MS) } } @@ -268,11 +269,11 @@ class Core( } val adsTelemetry by lazyMonitored { - AdsTelemetry(metrics) + AdsTelemetry() } val searchTelemetry by lazyMonitored { - InContentTelemetry(metrics) + InContentTelemetry() } /** @@ -408,11 +409,13 @@ class Core( private val passwordsEncryptionKey by lazyMonitored { getSecureAbove22Preferences().getString(PASSWORDS_KEY) ?: generateEncryptionKey(KEY_STRENGTH).also { - if (context.settings().passwordsEncryptionKeyGenerated && - isSentryEnabled() - ) { + if (context.settings().passwordsEncryptionKeyGenerated) { // 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") + crashReporter.submitCaughtException( + IllegalStateException( + "Passwords encryption key for passwords storage was lost and we generated a new one" + ) + ) } context.settings().recordPasswordsEncryptionKeyGenerated() getSecureAbove22Preferences().putString(PASSWORDS_KEY, it) @@ -427,7 +430,7 @@ class Core( 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 @@ -441,5 +444,6 @@ class Core( private const val KEY_STORAGE_NAME = "core_prefs" private const val PASSWORDS_KEY = "passwords" private const val RECENTLY_CLOSED_MAX = 10 + private const val HISTORY_METADATA_MAX_AGE_IN_MS = 14 * 24 * 60 * 60 * 1000 // 14 days } } diff --git a/app/src/main/java/org/mozilla/fenix/components/IntentProcessorType.kt b/app/src/main/java/org/mozilla/fenix/components/IntentProcessorType.kt index ea4dca70e..80f452a3c 100644 --- a/app/src/main/java/org/mozilla/fenix/components/IntentProcessorType.kt +++ b/app/src/main/java/org/mozilla/fenix/components/IntentProcessorType.kt @@ -39,10 +39,10 @@ enum class IntentProcessorType { fun IntentProcessors.getType(processor: IntentProcessor?) = when { migrationIntentProcessor == processor -> IntentProcessorType.MIGRATION externalAppIntentProcessors.contains(processor) || - customTabIntentProcessor == processor || - privateCustomTabIntentProcessor == processor -> IntentProcessorType.EXTERNAL_APP + customTabIntentProcessor == processor || + privateCustomTabIntentProcessor == processor -> IntentProcessorType.EXTERNAL_APP intentProcessor == processor || - privateIntentProcessor == processor || - fennecPageShortcutIntentProcessor == processor -> IntentProcessorType.NEW_TAB + privateIntentProcessor == processor || + fennecPageShortcutIntentProcessor == processor -> IntentProcessorType.NEW_TAB else -> IntentProcessorType.OTHER } diff --git a/app/src/main/java/org/mozilla/fenix/components/IntentProcessors.kt b/app/src/main/java/org/mozilla/fenix/components/IntentProcessors.kt index cf80fb2b0..9dfef2913 100644 --- a/app/src/main/java/org/mozilla/fenix/components/IntentProcessors.kt +++ b/app/src/main/java/org/mozilla/fenix/components/IntentProcessors.kt @@ -66,13 +66,13 @@ class IntentProcessors( val externalAppIntentProcessors by lazyMonitored { listOf( TrustedWebActivityIntentProcessor( - addNewTabUseCase = tabsUseCases.addTab, + addNewTabUseCase = customTabsUseCases.add, packageManager = context.packageManager, relationChecker = relationChecker, store = customTabsStore ), - WebAppIntentProcessor(store, tabsUseCases.addTab, sessionUseCases.loadUrl, manifestStorage), - FennecWebAppIntentProcessor(context, tabsUseCases.addTab, manifestStorage) + WebAppIntentProcessor(store, customTabsUseCases.addWebApp, sessionUseCases.loadUrl, manifestStorage), + FennecWebAppIntentProcessor(context, customTabsUseCases, manifestStorage) ) } diff --git a/app/src/main/java/org/mozilla/fenix/components/PermissionStorage.kt b/app/src/main/java/org/mozilla/fenix/components/PermissionStorage.kt index 6a3f6940a..c9d1d3e38 100644 --- a/app/src/main/java/org/mozilla/fenix/components/PermissionStorage.kt +++ b/app/src/main/java/org/mozilla/fenix/components/PermissionStorage.kt @@ -9,8 +9,8 @@ import androidx.annotation.VisibleForTesting import androidx.paging.DataSource import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import mozilla.components.feature.sitepermissions.SitePermissions -import mozilla.components.feature.sitepermissions.SitePermissionsStorage +import mozilla.components.concept.engine.permission.SitePermissions +import mozilla.components.concept.engine.permission.SitePermissionsStorage import org.mozilla.fenix.ext.components import org.mozilla.fenix.utils.Mockable import kotlin.coroutines.CoroutineContext @@ -20,7 +20,7 @@ class PermissionStorage( private val context: Context, @VisibleForTesting internal val dispatcher: CoroutineContext = Dispatchers.IO, @VisibleForTesting internal val permissionsStorage: SitePermissionsStorage = - context.components.sitePermissionsStorage + context.components.core.geckoSitePermissionsStorage ) { suspend fun add(sitePermissions: SitePermissions) = withContext(dispatcher) { @@ -35,7 +35,7 @@ class PermissionStorage( permissionsStorage.update(sitePermissions) } - fun getSitePermissionsPaged(): DataSource.Factory { + suspend fun getSitePermissionsPaged(): DataSource.Factory { return permissionsStorage.getSitePermissionsPaged() } diff --git a/app/src/main/java/org/mozilla/fenix/components/PrivateShortcutCreateManager.kt b/app/src/main/java/org/mozilla/fenix/components/PrivateShortcutCreateManager.kt index 95c840e92..9aa3d6364 100644 --- a/app/src/main/java/org/mozilla/fenix/components/PrivateShortcutCreateManager.kt +++ b/app/src/main/java/org/mozilla/fenix/components/PrivateShortcutCreateManager.kt @@ -38,15 +38,17 @@ object PrivateShortcutCreateManager { ) ) .setIcon(icon) - .setIntent(Intent(context, HomeActivity::class.java).apply { - action = Intent.ACTION_VIEW - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - putExtra(HomeActivity.PRIVATE_BROWSING_MODE, true) - putExtra( - HomeActivity.OPEN_TO_SEARCH, - StartSearchIntentProcessor.PRIVATE_BROWSING_PINNED_SHORTCUT - ) - }) + .setIntent( + Intent(context, HomeActivity::class.java).apply { + action = Intent.ACTION_VIEW + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + putExtra(HomeActivity.PRIVATE_BROWSING_MODE, true) + putExtra( + HomeActivity.OPEN_TO_SEARCH, + StartSearchIntentProcessor.PRIVATE_BROWSING_PINNED_SHORTCUT + ) + } + ) .build() val homeScreenIntent = Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME) diff --git a/app/src/main/java/org/mozilla/fenix/components/ReviewPromptController.kt b/app/src/main/java/org/mozilla/fenix/components/ReviewPromptController.kt index 2e8c185be..9793c0112 100644 --- a/app/src/main/java/org/mozilla/fenix/components/ReviewPromptController.kt +++ b/app/src/main/java/org/mozilla/fenix/components/ReviewPromptController.kt @@ -30,7 +30,7 @@ class FenixReviewSettings( get() = settings.numberOfAppLaunches set(value) { settings.numberOfAppLaunches = value } override val isDefaultBrowser: Boolean - get() = settings.isDefaultBrowser() + get() = settings.isDefaultBrowserBlocking() override var lastReviewPromptTimeInMillis: Long get() = settings.lastReviewPromptTimeInMillis set(value) { settings.lastReviewPromptTimeInMillis = value } diff --git a/app/src/main/java/org/mozilla/fenix/components/Services.kt b/app/src/main/java/org/mozilla/fenix/components/Services.kt index a9405285f..d8922d0b4 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Services.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Services.kt @@ -41,7 +41,8 @@ class Services( interceptLinkClicks = true, launchInApp = { PreferenceManager.getDefaultSharedPreferences(context).getBoolean( - context.getPreferenceKey(R.string.pref_key_open_links_in_external_app), false) + context.getPreferenceKey(R.string.pref_key_open_links_in_external_app), false + ) } ) } diff --git a/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt b/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt index cbd15afa4..da337195b 100644 --- a/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt +++ b/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt @@ -11,16 +11,17 @@ import androidx.lifecycle.asLiveData import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import mozilla.components.browser.state.state.TabSessionState import mozilla.components.feature.tab.collections.Tab import mozilla.components.feature.tab.collections.TabCollection import mozilla.components.feature.tab.collections.TabCollectionStorage import mozilla.components.support.base.observer.Observable import mozilla.components.support.base.observer.ObserverRegistry -import org.mozilla.fenix.perf.StrictModeManager import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.toShortUrl import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder +import org.mozilla.fenix.perf.StrictModeManager import org.mozilla.fenix.utils.Mockable @Mockable @@ -59,15 +60,21 @@ class TabCollectionStorage( } } - suspend fun createCollection(title: String, sessions: List) = ioScope.launch { - val id = collectionStorage.createCollection(title, sessions) - notifyObservers { onCollectionCreated(title, sessions, id) } - }.join() + suspend fun createCollection(title: String, sessions: List): Long? { + return withContext(ioScope.coroutineContext) { + val id = collectionStorage.createCollection(title, sessions) + notifyObservers { onCollectionCreated(title, sessions, id) } + id + } + } - suspend fun addTabsToCollection(tabCollection: TabCollection, sessions: List) = ioScope.launch { - collectionStorage.addTabsToCollection(tabCollection, sessions) - notifyObservers { onTabsAdded(tabCollection, sessions) } - }.join() + suspend fun addTabsToCollection(tabCollection: TabCollection, sessions: List): Long? { + return withContext(ioScope.coroutineContext) { + val id = collectionStorage.addTabsToCollection(tabCollection, sessions) + notifyObservers { onTabsAdded(tabCollection, sessions) } + id + } + } fun getCollections(): LiveData> { return collectionStorage.getCollections().asLiveData() 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 849a3a973..76441d6d3 100644 --- a/app/src/main/java/org/mozilla/fenix/components/UseCases.kt +++ b/app/src/main/java/org/mozilla/fenix/components/UseCases.kt @@ -5,7 +5,6 @@ package org.mozilla.fenix.components import android.content.Context -import mozilla.components.browser.session.SessionManager import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.Engine import mozilla.components.concept.storage.BookmarksStorage @@ -36,7 +35,6 @@ import org.mozilla.fenix.utils.Mockable class UseCases( private val context: Context, private val engine: Engine, - private val sessionManager: SessionManager, private val store: BrowserStore, private val shortcutManager: WebAppShortcutManager, private val topSitesStorage: TopSitesStorage, @@ -45,18 +43,18 @@ class UseCases( /** * Use cases that provide engine interactions for a given browser session. */ - val sessionUseCases by lazyMonitored { SessionUseCases(store, sessionManager) } + val sessionUseCases by lazyMonitored { SessionUseCases(store) } /** * Use cases that provide tab management. */ - val tabsUseCases: TabsUseCases by lazyMonitored { TabsUseCases(store, sessionManager) } + val tabsUseCases: TabsUseCases by lazyMonitored { TabsUseCases(store) } /** * Use cases for managing custom tabs. */ val customTabsUseCases: CustomTabsUseCases by lazyMonitored { - CustomTabsUseCases(sessionManager, sessionUseCases.loadUrl) + CustomTabsUseCases(store, sessionUseCases.loadUrl) } /** diff --git a/app/src/main/java/org/mozilla/fenix/components/accounts/FenixAccountManager.kt b/app/src/main/java/org/mozilla/fenix/components/accounts/FenixAccountManager.kt index fe5397bd1..cd83991ef 100644 --- a/app/src/main/java/org/mozilla/fenix/components/accounts/FenixAccountManager.kt +++ b/app/src/main/java/org/mozilla/fenix/components/accounts/FenixAccountManager.kt @@ -5,29 +5,55 @@ package org.mozilla.fenix.components.accounts import android.content.Context -import mozilla.components.service.fxa.manager.FxaAccountManager import org.mozilla.fenix.ext.components /** - * Component which holds a reference to [FxaAccountManager]. Manages account authentication, - * profiles, and profile state observers. + * Contains helper methods for querying Firefox Account state and its properties. */ -open class FenixAccountManager(context: Context) { - val accountManager = context.components.backgroundServices.accountManager +class FenixAccountManager(context: Context) { + private val accountManager = context.components.backgroundServices.accountManager - val authenticatedAccount - get() = accountManager.authenticatedAccount() != null + /** + * Returns the Firefox Account email if authenticated in the app, `null` otherwise. + */ + val accountProfileEmail: String? + get() = if (accountState == AccountState.AUTHENTICATED) { + accountManager.accountProfile()?.email + } else { + null + } + + /** + * The current state of the Firefox Account. See [AccountState]. + */ + val accountState: AccountState + get() = if (accountManager.authenticatedAccount() == null) { + AccountState.NO_ACCOUNT + } else { + if (accountManager.accountNeedsReauth()) { + AccountState.NEEDS_REAUTHENTICATION + } else { + AccountState.AUTHENTICATED + } + } +} - val accountProfileEmail - get() = accountManager.accountProfile()?.email +/** + * General states as an overview of the current Firefox Account. + */ +enum class AccountState { + /** + * There is no known Firefox Account. + */ + NO_ACCOUNT, /** - * Check if the current account is signed in and authenticated. + * A Firefox Account exists but needs to be re-authenticated. */ - fun signedInToFxa(): Boolean { - val account = accountManager.authenticatedAccount() - val needsReauth = accountManager.accountNeedsReauth() + NEEDS_REAUTHENTICATION, - return account != null && !needsReauth - } + /** + * A Firefox Account exists and the user is currently signed into it. + */ + AUTHENTICATED, } diff --git a/app/src/main/java/org/mozilla/fenix/components/bookmarks/BookmarksUseCase.kt b/app/src/main/java/org/mozilla/fenix/components/bookmarks/BookmarksUseCase.kt index e381312fa..895dda3be 100644 --- a/app/src/main/java/org/mozilla/fenix/components/bookmarks/BookmarksUseCase.kt +++ b/app/src/main/java/org/mozilla/fenix/components/bookmarks/BookmarksUseCase.kt @@ -6,10 +6,12 @@ package org.mozilla.fenix.components.bookmarks import androidx.annotation.WorkerThread import mozilla.appservices.places.BookmarkRoot +import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarksStorage +import java.util.concurrent.TimeUnit /** - * Use cases that allow for modifying bookmarks. + * Use cases that allow for modifying and retrieving bookmarks. */ class BookmarksUseCase(storage: BookmarksStorage) { @@ -38,5 +40,29 @@ class BookmarksUseCase(storage: BookmarksStorage) { } } + class RetrieveRecentBookmarksUseCase internal constructor( + private val storage: BookmarksStorage + ) { + /** + * Retrieves a list of recently added bookmarks, if any, up to maximum. + */ + @WorkerThread + suspend operator fun invoke( + count: Int = DEFAULT_BOOKMARKS_TO_RETRIEVE, + maxAgeInMs: Long = TimeUnit.DAYS.toMillis(DEFAULT_BOOKMARKS_DAYS_AGE_TO_RETRIEVE) + ): List { + return storage.getRecentBookmarks( + count, + maxAgeInMs + ) + } + } + val addBookmark by lazy { AddBookmarksUseCase(storage) } + val retrieveRecentBookmarks by lazy { RetrieveRecentBookmarksUseCase(storage) } + + companion object { + const val DEFAULT_BOOKMARKS_TO_RETRIEVE = 4 + const val DEFAULT_BOOKMARKS_DAYS_AGE_TO_RETRIEVE = 10L + } } diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/ActivationPing.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/ActivationPing.kt index 7b8dc8b8a..5d8492a27 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/ActivationPing.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/ActivationPing.kt @@ -25,7 +25,8 @@ class ActivationPing(private val context: Context) { private val prefs: SharedPreferences by lazy { context.getSharedPreferences( - "${this.javaClass.canonicalName}.prefs", Context.MODE_PRIVATE) + "${this.javaClass.canonicalName}.prefs", Context.MODE_PRIVATE + ) } /** diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/AdjustMetricsService.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/AdjustMetricsService.kt index baf4566d8..18c0906e0 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/AdjustMetricsService.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/AdjustMetricsService.kt @@ -67,6 +67,7 @@ class AdjustMetricsService(private val application: Application) : MetricsServic override fun stop() { Adjust.setEnabled(false) + Adjust.gdprForgetMe(application.applicationContext) } // We're not currently sending events directly to Adjust diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/AppLaunchTimeMeasurement.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/AppLaunchTimeMeasurement.kt deleted file mode 100644 index 548104555..000000000 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/AppLaunchTimeMeasurement.kt +++ /dev/null @@ -1,80 +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.os.Process -import android.os.SystemClock -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -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.WARM -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.HOT -import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.COLD -import org.mozilla.fenix.perf.Stat - -/** - * Handles the logic of figuring out launch time for cold, warm and hot startup. - */ -class AppLaunchTimeMeasurement(private val stats: Stat = Stat()) { - - private var isOnPreDrawCalled = false - - private var applicationOnCreateTimeStampNanoSeconds: Long? = null - private var homeActivityInitTimeStampNanoSeconds: Long? = null - private var homeActivityOnRestartTimeStampNanoSeconds: Long? = null - // we are considering screen to be visible after the first pre draw call. - private var homeActivityOnPreDrawTimeStampNanoSeconds: Long? = null - - fun onHomeActivityOnCreate(activityInitNanos: Long) { - this.homeActivityInitTimeStampNanoSeconds = activityInitNanos - } - - fun onHomeActivityOnRestart(activityOnRestartNanos: Long = SystemClock.elapsedRealtimeNanos()) { - homeActivityOnRestartTimeStampNanoSeconds = activityOnRestartNanos - } - - fun onFirstFramePreDraw(activityOnPreDrawNanos: Long = SystemClock.elapsedRealtimeNanos()) { - isOnPreDrawCalled = true - homeActivityOnPreDrawTimeStampNanoSeconds = activityOnPreDrawNanos - } - - /** - * if we have both start and finish time for launch, return the difference otherwise return null. - */ - suspend fun getApplicationLaunchTime(startupType: Type): Long? = withContext(Dispatchers.IO) { - when { - // one use case is user launching the app and quicky pressing back button. in that case - // there will be no onPredraw call but activity will call onStop(). - !isOnPreDrawCalled -> { - null - } - else -> { - when (startupType) { - COLD -> { - applicationOnCreateTimeStampNanoSeconds = - stats.getProcessStartTimeStampNano(Process.myPid()) - homeActivityOnPreDrawTimeStampNanoSeconds!!.minus( - applicationOnCreateTimeStampNanoSeconds!! - ) - } - WARM -> { - homeActivityOnPreDrawTimeStampNanoSeconds!!.minus( - homeActivityInitTimeStampNanoSeconds!! - ) - } - HOT -> { - homeActivityOnPreDrawTimeStampNanoSeconds!!.minus( - homeActivityOnRestartTimeStampNanoSeconds!! - ) - } - ERROR -> { - null - } - } - } - } - } -} 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 deleted file mode 100644 index 333fda67f..000000000 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/AppStartupTelemetry.kt +++ /dev/null @@ -1,199 +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 android.view.View -import androidx.annotation.VisibleForTesting -import androidx.core.view.doOnPreDraw -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 -import org.mozilla.fenix.perf.runBlockingIncrement -import java.lang.reflect.Modifier.PRIVATE - -/** - * Tracks application startup source, type, launch time, and whether or not activity has - * savedInstance to restore the activity from. - * Sample = [source = COLD, type = APP_ICON, hasSavedInstanceState = false,launchTimeNanoSeconds = 1824000000] - * The basic idea is to collect these metrics from different phases of startup through - * [AppAllStartup] and finally report them on Activity's onResume() function. - * - * **THIS CLASS HAS A KNOWN FLAW:** for COLD start, it doesn't take into account if the process is - * already running when the app starts, possibly inflating results (e.g. a Service started the - * process 20min ago and only now is HomeActivity launching). Future telemetry implementations should - * probably move in the ideological direction of [org.mozilla.fenix.perf.ColdStartupDurationTelemetry]: - * simplicity rather than comprehensiveness. - */ -@Suppress("TooManyFunctions") -class AppStartupTelemetry( - private val metrics: MetricController, - @VisibleForTesting(otherwise = PRIVATE) - var appLaunchTimeMeasurement: AppLaunchTimeMeasurement = AppLaunchTimeMeasurement() -) : 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, - homeActivityInitTimeStampNanoSeconds: Long, - rootContainer: View - ) { - setOnCreateData(safeIntent, hasSavedInstanceState, homeActivityInitTimeStampNanoSeconds, false) - rootContainer.doOnPreDraw { - onPreDraw() - } - } - - fun onExternalAppBrowserOnCreate( - safeIntent: SafeIntent, - hasSavedInstanceState: Boolean, - homeActivityInitTimeStampNanoSeconds: Long, - rootContainer: View - ) { - setOnCreateData(safeIntent, hasSavedInstanceState, homeActivityInitTimeStampNanoSeconds, true) - rootContainer.doOnPreDraw { - onPreDraw() - } - } - - fun onHomeActivityOnRestart(rootContainer: View) { - // DO NOT MOVE ANYTHING ABOVE THIS.. - // we are measuring startup time for hot startup type - appLaunchTimeMeasurement.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) - - rootContainer.doOnPreDraw { - onPreDraw() - } - } - - 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, - homeActivityInitTimeStampNanoSeconds: Long, - isExternalAppBrowserActivity: Boolean - ) { - onCreateData = AppAllStartup( - getStartupSourceFromIntent(safeIntent, isExternalAppBrowserActivity), - getAppStartupType(), - hasSavedInstanceState - ) - appLaunchTimeMeasurement.onHomeActivityOnCreate(homeActivityInitTimeStampNanoSeconds) - 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 - } - } - - private suspend fun recordMetric() { - if (!isMetricRecordedSinceAppWasForegrounded) { - val appAllStartup: AppAllStartup = if (onCreateData != null) { - onCreateData!! - } else { - mergeOnRestartAndOnNewIntentIntoStartup() - } - appAllStartup.launchTime = appLaunchTimeMeasurement.getApplicationLaunchTime(appAllStartup.type) - 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 - appLaunchTimeMeasurement = AppLaunchTimeMeasurement() - } - - 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 - } - - /** - *record the timestamp for the first frame drawn - */ - @VisibleForTesting(otherwise = PRIVATE) - fun onPreDraw() { - // DO NOT MOVE ANYTHING ABOVE THIS.. - // we are measuring startup time here. - appLaunchTimeMeasurement.onFirstFramePreDraw() - } - - /** - * record the metrics, blocking the main thread to make sure we get our metrics recorded before - * the application potentially closes. - */ - fun onStop() { - runBlockingIncrement { - recordMetric() - } - } -} 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 aff2f88d8..ed9bb43dc 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 @@ -21,7 +21,6 @@ import org.mozilla.fenix.GleanMetrics.Logins import org.mozilla.fenix.GleanMetrics.Onboarding import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp import org.mozilla.fenix.GleanMetrics.SearchShortcuts -import org.mozilla.fenix.GleanMetrics.Tip import org.mozilla.fenix.GleanMetrics.ToolbarSettings import org.mozilla.fenix.GleanMetrics.TopSites import org.mozilla.fenix.GleanMetrics.TrackingProtection @@ -52,7 +51,6 @@ sealed class Event { object CustomTabsClosed : Event() object CustomTabsActionTapped : Event() object CustomTabsMenuOpened : Event() - object UriOpened : Event() object NormalAndPrivateUriOpened : Event() object SyncAuthOpened : Event() object SyncAuthClosed : Event() @@ -96,17 +94,6 @@ sealed class Event { object CollectionRenamePressed : Event() object SearchWidgetNewTabPressed : Event() object SearchWidgetVoiceSearchPressed : Event() - object FindInPageOpened : Event() - object FindInPageClosed : Event() - object FindInPageSearchCommitted : Event() - object PrivateBrowsingSnackbarUndoTapped : Event() - object PrivateBrowsingNotificationTapped : Event() - object PrivateBrowsingCreateShortcut : Event() - object PrivateBrowsingAddShortcutCFR : Event() - object PrivateBrowsingCancelCFR : Event() - object PrivateBrowsingPinnedShortcutPrivateTab : Event() - object PrivateBrowsingStaticShortcutTab : Event() - object PrivateBrowsingStaticShortcutPrivateTab : Event() object TabMediaPlay : Event() object TabMediaPause : Event() object MediaPlayState : Event() @@ -114,17 +101,6 @@ sealed class Event { object MediaStopState : Event() object MediaFullscreenState : Event() object MediaPictureInPictureState : Event() - object InAppNotificationDownloadOpen : Event() - object InAppNotificationDownloadTryAgain : Event() - object NotificationDownloadCancel : Event() - object NotificationDownloadOpen : Event() - object NotificationDownloadPause : Event() - object NotificationDownloadResume : Event() - object NotificationDownloadTryAgain : Event() - object DownloadAdded : Event() - object DownloadsScreenOpened : Event() - object DownloadsItemOpened : Event() - object DownloadsItemDeleted : Event() object NotificationMediaPlay : Event() object NotificationMediaPause : Event() object TopSiteOpenDefault : Event() @@ -146,12 +122,7 @@ sealed class Event { object EditLogin : Event() object EditLoginSave : Event() object ViewLoginPassword : Event() - object CustomEngineAdded : Event() - object CustomEngineDeleted : Event() - object PrivateBrowsingShowSearchSuggestions : Event() object WhatsNewTapped : Event() - object SupportTapped : Event() - object PrivacyNoticeTapped : Event() object PocketTopSiteClicked : Event() object PocketTopSiteRemoved : Event() object FennecToFenixMigrated : Event() @@ -164,6 +135,7 @@ sealed class Event { object OnboardingPrivateBrowsing : Event() object OnboardingFinish : Event() object ChangedToDefaultBrowser : Event() + object DefaultBrowserNotifTapped : Event() object LoginDialogPromptDisplayed : Event() object LoginDialogPromptCancelled : Event() @@ -189,15 +161,10 @@ sealed class Event { object TabsTraySaveToCollectionPressed : Event() object TabsTrayShareAllTabsPressed : Event() object TabsTrayCloseAllTabsPressed : Event() - object TabsTrayCfrDismissed : Event() - object TabsTrayCfrTapped : Event() object ProgressiveWebAppOpenFromHomescreenTap : Event() object ProgressiveWebAppInstallAsShortcut : Event() - object MasterPasswordMigrationSuccess : Event() - object MasterPasswordMigrationDisplayed : Event() - object TabSettingsOpened : Event() object CopyUrlUsed : Event() @@ -208,10 +175,6 @@ sealed class Event { object HaveOpenTabs : Event() object HaveNoOpenTabs : Event() - object BannerOpenInAppDisplayed : Event() - object BannerOpenInAppDismissed : Event() - object BannerOpenInAppGoToSettings : Event() - object ContextMenuCopyTapped : Event() object ContextMenuSearchTapped : Event() object ContextMenuSelectAllTapped : Event() @@ -236,6 +199,28 @@ sealed class Event { object HomeMenuSettingsItemClicked : Event() object HomeScreenDisplayed : Event() + // Browser Toolbar + object BrowserToolbarHomeButtonClicked : Event() + + // Start on Home + object StartOnHomeEnterHomeScreen : Event() + object StartOnHomeOpenTabsTray : Event() + + // Recent tabs + object ShowAllRecentTabs : Event() + object OpenRecentTab : Event() + object OpenInProgressMediaTab : Event() + + // Android Autofill + object AndroidAutofillUnlockSuccessful : Event() + object AndroidAutofillUnlockCanceled : Event() + object AndroidAutofillSearchDisplayed : Event() + object AndroidAutofillSearchItemSelected : Event() + object AndroidAutofillConfirmationSuccessful : Event() + object AndroidAutofillConfirmationCanceled : Event() + object AndroidAutofillRequestWithLogins : Event() + object AndroidAutofillRequestWithoutLogins : Event() + // Interaction events with extras data class TopSiteSwipeCarousel(val page: Int) : Event() { @@ -342,21 +327,6 @@ sealed class Event { get() = hashMapOf(Addons.openAddonSettingKeys.addonId to addonId) } - data class TipDisplayed(val identifier: String) : Event() { - override val extras: Map? - get() = hashMapOf(Tip.displayedKeys.identifier to identifier) - } - - data class TipPressed(val identifier: String) : Event() { - override val extras: Map? - get() = hashMapOf(Tip.pressedKeys.identifier to identifier) - } - - data class TipClosed(val identifier: String) : Event() { - override val extras: Map? - get() = hashMapOf(Tip.closedKeys.identifier to identifier) - } - data class ToolbarPositionChanged(val position: Position) : Event() { enum class Position { TOP, BOTTOM } @@ -392,43 +362,6 @@ sealed class Event { get() = hashMapOf(Events.appOpenedKeys.source to source.name) } - data class AppReceivedIntent(val source: Source) : Event() { - - enum class Source { APP_ICON, LINK, CUSTOM_TAB, UNKNOWN } - - override val extras: Map? - get() = hashMapOf(Events.appOpenedAllStartupKeys.source to source.name) - } - - data class AppAllStartup( - val source: Source, - val type: Type, - val hasSavedInstanceState: Boolean? = null, - var launchTime: Long? = null - ) : Event() { - enum class Source { APP_ICON, LINK, CUSTOM_TAB, UNKNOWN } - enum class Type { COLD, WARM, HOT, ERROR } - - override val extras: Map? - 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() - } - if (launchTime != null) { - extrasMap[Events.appOpenedAllStartupKeys.firstFramePreDrawNanos] = - launchTime.toString() - } - return extrasMap - } - } - data class CollectionSaveButtonPressed(val fromScreen: String) : Event() { override val extras: Map? get() = mapOf(Collections.saveButtonKeys.fromScreen to fromScreen) @@ -517,7 +450,7 @@ sealed class Event { } val countLabel: String - get() = "${engineSource.identifier.toLowerCase(Locale.getDefault())}.$label" + get() = "${engineSource.identifier.lowercase(Locale.getDefault())}.$label" val sourceLabel: String get() = "${engineSource.descriptor}.$label" @@ -598,7 +531,7 @@ sealed class Event { } override val extras: Map? - get() = mapOf(Events.browserMenuActionKeys.item to item.toString().toLowerCase(Locale.ROOT)) + get() = mapOf(Events.browserMenuActionKeys.item to item.toString().lowercase(Locale.ROOT)) } data class TabCounterMenuItemTapped(val item: Item) : Event() { @@ -607,7 +540,7 @@ sealed class Event { } override val extras: Map? - get() = mapOf(Events.tabCounterMenuActionKeys.item to item.toString().toLowerCase(Locale.ROOT)) + get() = mapOf(Events.tabCounterMenuActionKeys.item to item.toString().lowercase(Locale.ROOT)) } object AutoPlaySettingVisited : Event() @@ -618,7 +551,14 @@ sealed class Event { } override val extras: Map? - get() = mapOf(Autoplay.settingChangedKeys.autoplaySetting to setting.toString().toLowerCase(Locale.ROOT)) + get() = mapOf(Autoplay.settingChangedKeys.autoplaySetting to setting.toString().lowercase(Locale.ROOT)) + } + + data class TabViewSettingChanged(val type: Type) : Event() { + enum class Type { LIST, GRID } + + override val extras: Map? + get() = mapOf(Events.tabViewChangedKeys.type to type.toString().lowercase(Locale.ROOT)) } sealed class Search 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 74f49ccfa..57800892a 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 @@ -8,13 +8,12 @@ import android.content.Context import mozilla.components.service.glean.Glean import mozilla.components.service.glean.private.NoExtraKeys import mozilla.components.support.base.log.logger.Logger -import org.mozilla.fenix.GleanMetrics.AboutPage import org.mozilla.fenix.GleanMetrics.Addons +import org.mozilla.fenix.GleanMetrics.AndroidAutofill import org.mozilla.fenix.GleanMetrics.AndroidKeystoreExperiment import org.mozilla.fenix.GleanMetrics.AppTheme import org.mozilla.fenix.GleanMetrics.Autoplay import org.mozilla.fenix.GleanMetrics.Awesomebar -import org.mozilla.fenix.GleanMetrics.BannerOpenInApp import org.mozilla.fenix.GleanMetrics.BookmarksManagement import org.mozilla.fenix.GleanMetrics.BrowserSearch import org.mozilla.fenix.GleanMetrics.Collections @@ -23,46 +22,37 @@ import org.mozilla.fenix.GleanMetrics.ContextualHintTrackingProtection import org.mozilla.fenix.GleanMetrics.ContextualMenu import org.mozilla.fenix.GleanMetrics.CrashReporter import org.mozilla.fenix.GleanMetrics.CustomTab -import org.mozilla.fenix.GleanMetrics.DownloadNotification -import org.mozilla.fenix.GleanMetrics.DownloadsMisc -import org.mozilla.fenix.GleanMetrics.DownloadsManagement import org.mozilla.fenix.GleanMetrics.ErrorPage import org.mozilla.fenix.GleanMetrics.Events import org.mozilla.fenix.GleanMetrics.ExperimentsDefaultBrowser -import org.mozilla.fenix.GleanMetrics.FindInPage import org.mozilla.fenix.GleanMetrics.History import org.mozilla.fenix.GleanMetrics.HomeMenu import org.mozilla.fenix.GleanMetrics.HomeScreen import org.mozilla.fenix.GleanMetrics.LoginDialog import org.mozilla.fenix.GleanMetrics.Logins -import org.mozilla.fenix.GleanMetrics.MasterPassword import org.mozilla.fenix.GleanMetrics.MediaNotification import org.mozilla.fenix.GleanMetrics.MediaState import org.mozilla.fenix.GleanMetrics.Metrics import org.mozilla.fenix.GleanMetrics.Onboarding import org.mozilla.fenix.GleanMetrics.Pings import org.mozilla.fenix.GleanMetrics.Pocket -import org.mozilla.fenix.GleanMetrics.PrivateBrowsingMode -import org.mozilla.fenix.GleanMetrics.PrivateBrowsingShortcut import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp import org.mozilla.fenix.GleanMetrics.ReaderMode +import org.mozilla.fenix.GleanMetrics.RecentTabs import org.mozilla.fenix.GleanMetrics.SearchShortcuts -import org.mozilla.fenix.GleanMetrics.SearchSuggestions import org.mozilla.fenix.GleanMetrics.SearchWidget import org.mozilla.fenix.GleanMetrics.SetDefaultNewtabExperiment import org.mozilla.fenix.GleanMetrics.SetDefaultSettingExperiment +import org.mozilla.fenix.GleanMetrics.StartOnHome import org.mozilla.fenix.GleanMetrics.SyncAccount import org.mozilla.fenix.GleanMetrics.SyncAuth import org.mozilla.fenix.GleanMetrics.SyncedTabs import org.mozilla.fenix.GleanMetrics.Tab import org.mozilla.fenix.GleanMetrics.Tabs import org.mozilla.fenix.GleanMetrics.TabsTray -import org.mozilla.fenix.GleanMetrics.TabsTrayCfr -import org.mozilla.fenix.GleanMetrics.Tip import org.mozilla.fenix.GleanMetrics.ToolbarSettings import org.mozilla.fenix.GleanMetrics.TopSites import org.mozilla.fenix.GleanMetrics.TrackingProtection -import org.mozilla.fenix.GleanMetrics.UserSpecifiedSearchEngines import org.mozilla.fenix.GleanMetrics.VoiceSearch import org.mozilla.fenix.ext.components @@ -82,7 +72,7 @@ private class EventWrapper>( if (index == 0) { builder.append(part) } else { - builder.append(part[0].toUpperCase()) + builder.append(part[0].uppercase()) builder.append(part.substring(1)) } } @@ -99,24 +89,20 @@ private class EventWrapper>( null } + @Suppress("DEPRECATION") + // FIXME(#19967): Migrate to non-deprecated API. this.recorder(extras) } } +@Suppress("DEPRECATION") +// FIXME(#19967): Migrate to non-deprecated API. private val Event.wrapper: EventWrapper<*>? get() = when (this) { is Event.OpenedApp -> EventWrapper( { Events.appOpened.record(it) }, { Events.appOpenedKeys.valueOf(it) } ) - is Event.AppReceivedIntent -> EventWrapper( - { Events.appReceivedIntent.record(it) }, - { Events.appReceivedIntentKeys.valueOf(it) } - ) - is Event.AppAllStartup -> EventWrapper( - { Events.appOpenedAllStartup.record(it) }, - { Events.appOpenedAllStartupKeys.valueOf(it) } - ) is Event.SearchBarTapped -> EventWrapper( { Events.searchBarTapped.record(it) }, { Events.searchBarTappedKeys.valueOf(it) } @@ -163,15 +149,6 @@ private val Event.wrapper: EventWrapper<*>? is Event.LoginDialogPromptNeverSave -> EventWrapper( { LoginDialog.neverSave.record(it) } ) - is Event.FindInPageOpened -> EventWrapper( - { FindInPage.opened.record(it) } - ) - is Event.FindInPageClosed -> EventWrapper( - { FindInPage.closed.record(it) } - ) - is Event.FindInPageSearchCommitted -> EventWrapper( - { FindInPage.searchedPage.record(it) } - ) is Event.ContextMenuItemTapped -> EventWrapper( { ContextMenu.itemTapped.record(it) }, { ContextMenu.itemTappedKeys.valueOf(it) } @@ -196,6 +173,9 @@ private val Event.wrapper: EventWrapper<*>? is Event.ChangedToDefaultBrowser -> EventWrapper( { Events.defaultBrowserChanged.record(it) } ) + is Event.DefaultBrowserNotifTapped -> EventWrapper( + { Events.defaultBrowserNotifTapped.record(it) } + ) is Event.OpenedBookmark -> EventWrapper( { BookmarksManagement.open.record(it) } ) @@ -244,9 +224,6 @@ private val Event.wrapper: EventWrapper<*>? is Event.CustomTabsClosed -> EventWrapper( { CustomTab.closed.record(it) } ) - is Event.UriOpened -> EventWrapper( - { Events.totalUriCount.add(1) } - ) is Event.NormalAndPrivateUriOpened -> EventWrapper( { Events.normalAndPrivateUriCount.add(1) } ) @@ -390,30 +367,6 @@ private val Event.wrapper: EventWrapper<*>? is Event.SearchWidgetVoiceSearchPressed -> EventWrapper( { SearchWidget.voiceButton.record(it) } ) - is Event.PrivateBrowsingSnackbarUndoTapped -> EventWrapper( - { PrivateBrowsingMode.snackbarUndo.record(it) } - ) - is Event.PrivateBrowsingNotificationTapped -> EventWrapper( - { PrivateBrowsingMode.notificationTapped.record(it) } - ) - is Event.PrivateBrowsingCreateShortcut -> EventWrapper( - { PrivateBrowsingShortcut.createShortcut.record(it) } - ) - is Event.PrivateBrowsingAddShortcutCFR -> EventWrapper( - { PrivateBrowsingShortcut.cfrAddShortcut.record(it) } - ) - is Event.PrivateBrowsingCancelCFR -> EventWrapper( - { PrivateBrowsingShortcut.cfrCancel.record(it) } - ) - is Event.PrivateBrowsingPinnedShortcutPrivateTab -> EventWrapper( - { PrivateBrowsingShortcut.pinnedShortcutPriv.record(it) } - ) - is Event.PrivateBrowsingStaticShortcutTab -> EventWrapper( - { PrivateBrowsingShortcut.staticShortcutTab.record(it) } - ) - is Event.PrivateBrowsingStaticShortcutPrivateTab -> EventWrapper( - { PrivateBrowsingShortcut.staticShortcutPriv.record(it) } - ) is Event.WhatsNewTapped -> EventWrapper( { Events.whatsNewTapped.record(it) } ) @@ -438,39 +391,6 @@ private val Event.wrapper: EventWrapper<*>? is Event.MediaPictureInPictureState -> EventWrapper( { MediaState.pictureInPicture.record(it) } ) - is Event.InAppNotificationDownloadOpen -> EventWrapper( - { DownloadNotification.inAppOpen.record(it) } - ) - is Event.InAppNotificationDownloadTryAgain -> EventWrapper( - { DownloadNotification.inAppTryAgain.record(it) } - ) - is Event.NotificationDownloadCancel -> EventWrapper( - { DownloadNotification.cancel.record(it) } - ) - is Event.NotificationDownloadOpen -> EventWrapper( - { DownloadNotification.open.record(it) } - ) - is Event.NotificationDownloadPause -> EventWrapper( - { DownloadNotification.pause.record(it) } - ) - is Event.NotificationDownloadResume -> EventWrapper( - { DownloadNotification.resume.record(it) } - ) - is Event.NotificationDownloadTryAgain -> EventWrapper( - { DownloadNotification.tryAgain.record(it) } - ) - is Event.DownloadAdded -> EventWrapper( - { DownloadsMisc.downloadAdded.record(it) } - ) - is Event.DownloadsScreenOpened -> EventWrapper( - { DownloadsManagement.downloadsScreenOpened.record(it) } - ) - is Event.DownloadsItemOpened -> EventWrapper( - { DownloadsManagement.itemOpened.record(it) } - ) - is Event.DownloadsItemDeleted -> EventWrapper( - { DownloadsManagement.itemDeleted.record(it) } - ) is Event.NotificationMediaPlay -> EventWrapper( { MediaNotification.play.record(it) } ) @@ -521,19 +441,10 @@ private val Event.wrapper: EventWrapper<*>? is Event.EditLoginSave -> EventWrapper( { Logins.saveEditedLogin.record(it) } ) - is Event.PrivateBrowsingShowSearchSuggestions -> EventWrapper( - { SearchSuggestions.enableInPrivate.record(it) } - ) is Event.ToolbarPositionChanged -> EventWrapper( { ToolbarSettings.changedPosition.record(it) }, { ToolbarSettings.changedPositionKeys.valueOf(it) } ) - is Event.CustomEngineAdded -> EventWrapper( - { UserSpecifiedSearchEngines.customEngineAdded.record(it) } - ) - is Event.CustomEngineDeleted -> EventWrapper( - { UserSpecifiedSearchEngines.customEngineDeleted.record(it) } - ) is Event.SaveLoginsSettingChanged -> EventWrapper( { Logins.saveLoginsSettingChanged.record(it) }, { Logins.saveLoginsSettingChangedKeys.valueOf(it) } @@ -567,12 +478,6 @@ private val Event.wrapper: EventWrapper<*>? { TopSites.swipeCarousel.record(it) }, { TopSites.swipeCarouselKeys.valueOf(it) } ) - is Event.SupportTapped -> EventWrapper( - { AboutPage.supportTapped.record(it) } - ) - is Event.PrivacyNoticeTapped -> EventWrapper( - { AboutPage.privacyNoticeTapped.record(it) } - ) is Event.PocketTopSiteClicked -> EventWrapper( { Pocket.pocketTopSiteClicked.record(it) } ) @@ -594,18 +499,6 @@ private val Event.wrapper: EventWrapper<*>? { Addons.openAddonSetting.record(it) }, { Addons.openAddonSettingKeys.valueOf(it) } ) - is Event.TipDisplayed -> EventWrapper( - { Tip.displayed.record(it) }, - { Tip.displayedKeys.valueOf(it) } - ) - is Event.TipPressed -> EventWrapper( - { Tip.pressed.record(it) }, - { Tip.pressedKeys.valueOf(it) } - ) - is Event.TipClosed -> EventWrapper( - { Tip.closed.record(it) }, - { Tip.closedKeys.valueOf(it) } - ) is Event.VoiceSearchTapped -> EventWrapper( { VoiceSearch.tapped.record(it) } ) @@ -696,12 +589,6 @@ private val Event.wrapper: EventWrapper<*>? is Event.TabsTrayCloseAllTabsPressed -> EventWrapper( { TabsTray.closeAllTabs.record(it) } ) - is Event.TabsTrayCfrDismissed -> EventWrapper( - { TabsTrayCfr.dismiss.record(it) } - ) - is Event.TabsTrayCfrTapped -> EventWrapper( - { TabsTrayCfr.goToSettings.record(it) } - ) is Event.AutoPlaySettingVisited -> EventWrapper( { Autoplay.visitedSetting.record(it) } ) @@ -735,12 +622,6 @@ private val Event.wrapper: EventWrapper<*>? { Events.recentlyClosedTabsOpened.record(it) } ) - is Event.MasterPasswordMigrationDisplayed -> EventWrapper( - { MasterPassword.displayed.record(it) } - ) - is Event.MasterPasswordMigrationSuccess -> EventWrapper( - { MasterPassword.migration.record(it) } - ) is Event.TabSettingsOpened -> EventWrapper( { Tabs.settingOpened.record(it) } ) @@ -762,15 +643,6 @@ private val Event.wrapper: EventWrapper<*>? Event.HaveNoOpenTabs -> EventWrapper( { Metrics.hasOpenTabs.set(false) } ) - is Event.BannerOpenInAppDisplayed -> EventWrapper( - { BannerOpenInApp.displayed.record(it) } - ) - is Event.BannerOpenInAppDismissed -> EventWrapper( - { BannerOpenInApp.dismissed.record(it) } - ) - is Event.BannerOpenInAppGoToSettings -> EventWrapper( - { BannerOpenInApp.goToSettings.record(it) } - ) is Event.SyncedTabSuggestionClicked -> EventWrapper( { SyncedTabs.syncedTabsSuggestionClicked.record(it) } ) @@ -832,6 +704,59 @@ private val Event.wrapper: EventWrapper<*>? is Event.HomeScreenDisplayed -> EventWrapper( { HomeScreen.homeScreenDisplayed.record(it) } ) + is Event.TabViewSettingChanged -> EventWrapper( + { Events.tabViewChanged.record(it) }, + { Events.tabViewChangedKeys.valueOf(it) } + ) + + is Event.BrowserToolbarHomeButtonClicked -> EventWrapper( + { Events.browserToolbarHomeTapped.record(it) } + ) + + is Event.StartOnHomeEnterHomeScreen -> EventWrapper( + { StartOnHome.enterHomeScreen.record(it) } + ) + + is Event.StartOnHomeOpenTabsTray -> EventWrapper( + { StartOnHome.openTabsTray.record(it) } + ) + + is Event.OpenRecentTab -> EventWrapper( + { RecentTabs.recentTabOpened.record(it) } + ) + + is Event.OpenInProgressMediaTab -> EventWrapper( + { RecentTabs.inProgressMediaTabOpened.record(it) } + ) + + is Event.ShowAllRecentTabs -> EventWrapper( + { RecentTabs.showAllClicked.record(it) } + ) + + is Event.AndroidAutofillRequestWithLogins -> EventWrapper( + { AndroidAutofill.requestMatchingLogins.record(it) } + ) + is Event.AndroidAutofillRequestWithoutLogins -> EventWrapper( + { AndroidAutofill.requestNoMatchingLogins.record(it) } + ) + is Event.AndroidAutofillSearchDisplayed -> EventWrapper( + { AndroidAutofill.searchDisplayed.record(it) } + ) + is Event.AndroidAutofillSearchItemSelected -> EventWrapper( + { AndroidAutofill.searchItemSelected.record(it) } + ) + is Event.AndroidAutofillUnlockCanceled -> EventWrapper( + { AndroidAutofill.unlockCancelled.record(it) } + ) + is Event.AndroidAutofillUnlockSuccessful -> EventWrapper( + { AndroidAutofill.unlockSuccessful.record(it) } + ) + is Event.AndroidAutofillConfirmationCanceled -> EventWrapper( + { AndroidAutofill.confirmCancelled.record(it) } + ) + is Event.AndroidAutofillConfirmationSuccessful -> EventWrapper( + { AndroidAutofill.confirmSuccessful.record(it) } + ) // Don't record other events in Glean: is Event.AddBookmark -> null 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 c38ebb62d..3a4387b09 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 @@ -9,6 +9,7 @@ import mozilla.components.browser.awesomebar.facts.BrowserAwesomeBarFacts import mozilla.components.browser.menu.facts.BrowserMenuFacts import mozilla.components.browser.toolbar.facts.ToolbarFacts import mozilla.components.concept.awesomebar.AwesomeBar +import mozilla.components.feature.autofill.facts.AutofillFacts import mozilla.components.feature.awesomebar.facts.AwesomeBarFacts import mozilla.components.feature.awesomebar.provider.BookmarksStorageSuggestionProvider import mozilla.components.feature.awesomebar.provider.ClipboardSuggestionProvider @@ -17,12 +18,11 @@ import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider import mozilla.components.feature.awesomebar.provider.SessionSuggestionProvider import mozilla.components.feature.contextmenu.facts.ContextMenuFacts import mozilla.components.feature.customtabs.CustomTabsFacts -import mozilla.components.feature.downloads.facts.DownloadsFacts -import mozilla.components.feature.findinpage.facts.FindInPageFacts import mozilla.components.feature.media.facts.MediaFacts import mozilla.components.feature.prompts.facts.LoginDialogFacts -import mozilla.components.feature.prompts.facts.CreditCardAutofillDialogFacts import mozilla.components.feature.pwa.ProgressiveWebAppFacts +import mozilla.components.feature.search.telemetry.ads.AdsTelemetry +import mozilla.components.feature.search.telemetry.incontent.InContentTelemetry import mozilla.components.feature.syncedtabs.facts.SyncedTabsFacts import mozilla.components.feature.top.sites.facts.TopSitesFacts import mozilla.components.lib.dataprotect.SecurePrefsReliabilityExperiment @@ -162,13 +162,7 @@ internal class ReleaseMetricController( Component.FEATURE_PROMPTS to LoginDialogFacts.Items.CANCEL -> Event.LoginDialogPromptCancelled Component.FEATURE_PROMPTS to LoginDialogFacts.Items.NEVER_SAVE -> Event.LoginDialogPromptNeverSave Component.FEATURE_PROMPTS to LoginDialogFacts.Items.SAVE -> Event.LoginDialogPromptSave - Component.FEATURE_PROMPTS to CreditCardAutofillDialogFacts.Items.AUTOFILL_CREDIT_CARD_SUCCESS -> { - settings.creditCardsAutofilledCount += 1 - null - } - Component.FEATURE_FINDINPAGE to FindInPageFacts.Items.CLOSE -> Event.FindInPageClosed - Component.FEATURE_FINDINPAGE to FindInPageFacts.Items.INPUT -> Event.FindInPageSearchCommitted Component.FEATURE_CONTEXTMENU to ContextMenuFacts.Items.ITEM -> { metadata?.get("item")?.let { Event.ContextMenuItemTapped.create(it.toString()) } } @@ -191,17 +185,6 @@ internal class ReleaseMetricController( Component.FEATURE_CUSTOMTABS to CustomTabsFacts.Items.CLOSE -> Event.CustomTabsClosed Component.FEATURE_CUSTOMTABS to CustomTabsFacts.Items.ACTION_BUTTON -> Event.CustomTabsActionTapped - Component.FEATURE_DOWNLOADS to DownloadsFacts.Items.NOTIFICATION -> { - when (action) { - Action.CANCEL -> Event.NotificationDownloadCancel - Action.OPEN -> Event.NotificationDownloadOpen - Action.PAUSE -> Event.NotificationDownloadPause - Action.RESUME -> Event.NotificationDownloadResume - Action.TRY_AGAIN -> Event.NotificationDownloadTryAgain - else -> null - } - } - Component.FEATURE_MEDIA to MediaFacts.Items.NOTIFICATION -> { when (action) { Action.PLAY -> Event.NotificationMediaPlay @@ -315,6 +298,44 @@ internal class ReleaseMetricController( Event.SecurePrefsReset } + Component.FEATURE_SEARCH to AdsTelemetry.SERP_ADD_CLICKED -> { + Event.SearchAdClicked(value!!) + } + Component.FEATURE_SEARCH to AdsTelemetry.SERP_SHOWN_WITH_ADDS -> { + Event.SearchWithAds(value!!) + } + Component.FEATURE_SEARCH to InContentTelemetry.IN_CONTENT_SEARCH -> { + Event.SearchInContent(value!!) + } + Component.FEATURE_AUTOFILL to AutofillFacts.Items.AUTOFILL_REQUEST -> { + val hasMatchingLogins = metadata?.get(AutofillFacts.Metadata.HAS_MATCHING_LOGINS) as Boolean? + if (hasMatchingLogins == true) { + Event.AndroidAutofillRequestWithLogins + } else { + Event.AndroidAutofillRequestWithoutLogins + } + } + Component.FEATURE_AUTOFILL to AutofillFacts.Items.AUTOFILL_SEARCH -> { + if (action == Action.SELECT) { + Event.AndroidAutofillSearchItemSelected + } else { + Event.AndroidAutofillSearchDisplayed + } + } + Component.FEATURE_AUTOFILL to AutofillFacts.Items.AUTOFILL_LOCK -> { + if (action == Action.CONFIRM) { + Event.AndroidAutofillUnlockSuccessful + } else { + Event.AndroidAutofillUnlockCanceled + } + } + Component.FEATURE_AUTOFILL to AutofillFacts.Items.AUTOFILL_CONFIRMATION -> { + if (action == Action.CONFIRM) { + Event.AndroidAutofillConfirmationSuccessful + } else { + Event.AndroidAutofillConfirmationCanceled + } + } else -> null } diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/SecurePrefsTelemetry.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/SecurePrefsTelemetry.kt index c88841f16..23c1998fa 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/SecurePrefsTelemetry.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/SecurePrefsTelemetry.kt @@ -27,7 +27,7 @@ class SecurePrefsTelemetry( experiments.withExperiment(FeatureId.ANDROID_KEYSTORE) { experimentBranch -> // .. and this device is not in the control group. if (experimentBranch == ExperimentBranch.TREATMENT) { - SecurePrefsReliabilityExperiment(appContext)() + SecurePrefsReliabilityExperiment(appContext)() } } } diff --git a/app/src/main/java/org/mozilla/fenix/components/tips/providers/MasterPasswordTipProvider.kt b/app/src/main/java/org/mozilla/fenix/components/tips/providers/MasterPasswordTipProvider.kt index 0c28b2c18..95e582962 100644 --- a/app/src/main/java/org/mozilla/fenix/components/tips/providers/MasterPasswordTipProvider.kt +++ b/app/src/main/java/org/mozilla/fenix/components/tips/providers/MasterPasswordTipProvider.kt @@ -14,26 +14,23 @@ import androidx.core.view.isVisible import com.google.android.material.button.MaterialButton import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputLayout -import io.sentry.Sentry import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import mozilla.appservices.logins.IdCollisionException -import mozilla.appservices.logins.InvalidRecordException -import mozilla.appservices.logins.LoginsStorageException -import mozilla.appservices.logins.ServerPassword -import mozilla.components.concept.storage.Login +import mozilla.components.service.sync.logins.IdCollisionException +import mozilla.components.service.sync.logins.InvalidRecordException +import mozilla.components.service.sync.logins.LoginsStorageException +import mozilla.components.service.sync.logins.ServerPassword +import mozilla.components.service.sync.logins.toLogin import mozilla.components.support.migration.FennecLoginsMPImporter import mozilla.components.support.migration.FennecProfile import org.mozilla.fenix.R -import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.tips.Tip import org.mozilla.fenix.components.tips.TipProvider import org.mozilla.fenix.components.tips.TipType import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.settings /** @@ -60,7 +57,7 @@ class MasterPasswordTipProvider( override val shouldDisplay: Boolean by lazy { context.settings().shouldDisplayMasterPasswordMigrationTip && - fennecLoginsMPImporter?.hasMasterPassword() == true + fennecLoginsMPImporter?.hasMasterPassword() == true } private fun masterPasswordMigrationTip(): Tip = @@ -88,8 +85,6 @@ class MasterPasswordTipProvider( val dialog = dialogBuilder.show() - context.metrics.track(Event.MasterPasswordMigrationDisplayed) - val passwordErrorText = context.getString(R.string.mp_dialog_error_transfer_saved_logins) val migrationContinueButton = dialogView.findViewById(R.id.migration_continue).apply { @@ -198,13 +193,13 @@ class MasterPasswordTipProvider( context.components.core.passwordsStorage.add(it) } catch (e: InvalidRecordException) { // This record was invalid and we couldn't save this login - Sentry.capture("Master Password migration add login error $e for reason ${e.reason}") + context.components.analytics.crashReporter.submitCaughtException(e) } catch (e: IdCollisionException) { // Nonempty ID was provided - Sentry.capture("Master Password migration add login error $e") + context.components.analytics.crashReporter.submitCaughtException(e) } catch (e: LoginsStorageException) { // Some other error occurred - Sentry.capture("Master Password migration add login error $e") + context.components.analytics.crashReporter.submitCaughtException(e) } } withContext(Dispatchers.Main) { @@ -217,8 +212,6 @@ class MasterPasswordTipProvider( private fun dismissMPTip() { tip?.let { - context.metrics.track(Event.TipClosed(it.identifier)) - context.components.settings.preferences .edit() .putBoolean(it.identifier, false) @@ -231,8 +224,6 @@ class MasterPasswordTipProvider( private fun showSuccessDialog() { dismissMPTip() - context.metrics.track(Event.MasterPasswordMigrationSuccess) - val dialogView = LayoutInflater.from(context).inflate(R.layout.mp_migration_done_dialog, null) @@ -258,23 +249,6 @@ class MasterPasswordTipProvider( } } - /** - * Converts an Application Services [ServerPassword] to an Android Components [Login] - */ - fun ServerPassword.toLogin() = Login( - origin = hostname, - formActionOrigin = formSubmitURL, - httpRealm = httpRealm, - username = username, - password = password, - timesUsed = timesUsed, - timeCreated = timeCreated, - timeLastUsed = timeLastUsed, - timePasswordChanged = timePasswordChanged, - usernameField = usernameField, - passwordField = passwordField - ) - companion object { private const val HALF_OPACITY = .5F } diff --git a/app/src/main/java/org/mozilla/fenix/components/tips/providers/MigrationTipProvider.kt b/app/src/main/java/org/mozilla/fenix/components/tips/providers/MigrationTipProvider.kt index f84c1e7ed..9eea0d6ed 100644 --- a/app/src/main/java/org/mozilla/fenix/components/tips/providers/MigrationTipProvider.kt +++ b/app/src/main/java/org/mozilla/fenix/components/tips/providers/MigrationTipProvider.kt @@ -92,9 +92,11 @@ class MigrationTipProvider(private val context: Context) : TipProvider { return if (MozillaProductDetector.packageIsInstalled(context, FENIX.productName)) { context.startActivity(context.packageManager.getLaunchIntentForPackage(FENIX.productName)) } else { - context.startActivity(Intent( - Intent.ACTION_VIEW, Uri.parse(SupportUtils.FIREFOX_NIGHTLY_PLAY_STORE_URL) - )) + context.startActivity( + Intent( + Intent.ACTION_VIEW, Uri.parse(SupportUtils.FIREFOX_NIGHTLY_PLAY_STORE_URL) + ) + ) } } diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserInteractor.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserInteractor.kt deleted file mode 100644 index 03d7971c8..000000000 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserInteractor.kt +++ /dev/null @@ -1,45 +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.toolbar - -import mozilla.components.ui.tabcounter.TabCounterMenu - -open class BrowserInteractor( - private val browserToolbarController: BrowserToolbarController, - private val menuController: BrowserToolbarMenuController -) : BrowserToolbarViewInteractor { - - override fun onTabCounterClicked() { - browserToolbarController.handleTabCounterClick() - } - - override fun onTabCounterMenuItemTapped(item: TabCounterMenu.Item) { - browserToolbarController.handleTabCounterItemInteraction(item) - } - - override fun onBrowserToolbarPaste(text: String) { - browserToolbarController.handleToolbarPaste(text) - } - - override fun onBrowserToolbarPasteAndGo(text: String) { - browserToolbarController.handleToolbarPasteAndGo(text) - } - - override fun onBrowserToolbarClicked() { - browserToolbarController.handleToolbarClick() - } - - override fun onBrowserToolbarMenuItemTapped(item: ToolbarMenu.Item) { - menuController.handleToolbarItemInteraction(item) - } - - override fun onScrolled(offset: Int) { - browserToolbarController.handleScroll(offset) - } - - override fun onReaderModePressed(enabled: Boolean) { - browserToolbarController.handleReaderModePressed(enabled) - } -} 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 fa303fc23..2686f0411 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 @@ -15,6 +15,7 @@ import mozilla.components.concept.engine.EngineView import mozilla.components.feature.tabs.TabsUseCases import mozilla.components.support.ktx.kotlin.isUrl import mozilla.components.ui.tabcounter.TabCounterMenu +import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.browser.BrowserAnimator.Companion.getToolbarNavOptions @@ -23,8 +24,8 @@ import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.browser.readermode.ReaderModeController import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController +import org.mozilla.fenix.components.toolbar.interactor.BrowserToolbarInteractor import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.navigateBlockingForAsyncNavGraph import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.settings import org.mozilla.fenix.home.HomeScreenViewModel @@ -40,6 +41,11 @@ interface BrowserToolbarController { fun handleTabCounterClick() fun handleTabCounterItemInteraction(item: TabCounterMenu.Item) fun handleReaderModePressed(enabled: Boolean) + + /** + * @see [BrowserToolbarInteractor.onHomeButtonClicked] + */ + fun handleHomeButtonClick() } class DefaultBrowserToolbarController( @@ -86,8 +92,12 @@ class DefaultBrowserToolbarController( override fun handleToolbarClick() { metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.BROWSER)) - navController.nav( - R.id.browserFragment, + if (FeatureFlags.showHomeBehindSearch) { + navController.navigate( + BrowserFragmentDirections.actionGlobalHome() + ) + } + navController.navigate( BrowserFragmentDirections.actionGlobalSearchDialog( currentSession?.id ), @@ -119,7 +129,7 @@ class DefaultBrowserToolbarController( // When closing the last tab we must show the undo snackbar in the home fragment if (store.state.getNormalOrPrivateTabs(it.content.private).count() == 1) { homeViewModel.sessionToDelete = it.id - navController.navigateBlockingForAsyncNavGraph( + navController.navigate( BrowserFragmentDirections.actionGlobalHome() ) } else { @@ -133,7 +143,7 @@ class DefaultBrowserToolbarController( Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.NEW_TAB) ) activity.browsingModeManager.mode = BrowsingMode.Normal - navController.navigateBlockingForAsyncNavGraph( + navController.navigate( BrowserFragmentDirections.actionGlobalHome(focusOnAddressBar = true) ) } @@ -144,7 +154,7 @@ class DefaultBrowserToolbarController( ) ) activity.browsingModeManager.mode = BrowsingMode.Private - navController.navigateBlockingForAsyncNavGraph( + navController.navigate( BrowserFragmentDirections.actionGlobalHome(focusOnAddressBar = true) ) } @@ -157,6 +167,14 @@ class DefaultBrowserToolbarController( } } + override fun handleHomeButtonClick() { + metrics.track(Event.BrowserToolbarHomeButtonClicked) + + navController.navigate( + BrowserFragmentDirections.actionGlobalHome() + ) + } + companion object { internal const val TELEMETRY_BROWSER_IDENTIFIER = "browserMenu" } @@ -167,8 +185,10 @@ private fun BrowserStore.updateSearchTermsOfSelectedSession( ) { val selectedTabId = state.selectedTabId ?: return - dispatch(ContentAction.UpdateSearchTermsAction( - selectedTabId, - searchTerms - )) + dispatch( + ContentAction.UpdateSearchTermsAction( + selectedTabId, + searchTerms + ) + ) } diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt index 8f7fdd0f0..aec201d84 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt @@ -35,11 +35,11 @@ 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.accounts.AccountState import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getRootView -import org.mozilla.fenix.ext.navigateBlockingForAsyncNavGraph import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.navigateSafe import org.mozilla.fenix.ext.openSetDefaultBrowserOption @@ -118,13 +118,15 @@ class DefaultBrowserToolbarMenuController( customTabUseCases.migrate(customTabSessionId, select = true) // Switch to the actual browser which should now display our new selected session - activity.startActivity(openInFenixIntent.apply { - // We never want to launch the browser in the same task as the external app - // activity. So we force a new task here. IntentReceiverActivity will do the - // right thing and take care of routing to an already existing browser and avoid - // cloning a new one. - flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK - }) + activity.startActivity( + openInFenixIntent.apply { + // We never want to launch the browser in the same task as the external app + // activity. So we force a new task here. IntentReceiverActivity will do the + // right thing and take care of routing to an already existing browser and avoid + // cloning a new one. + flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK + } + ) // Close this activity (and the task) since it is no longer displaying any session activity.finishAndRemoveTask() @@ -163,7 +165,7 @@ class DefaultBrowserToolbarMenuController( } is ToolbarMenu.Item.Back -> { if (item.viewHistory) { - navController.navigateBlockingForAsyncNavGraph( + navController.navigate( BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment( activeSessionId = customTabSessionId ) @@ -176,7 +178,7 @@ class DefaultBrowserToolbarMenuController( } is ToolbarMenu.Item.Forward -> { if (item.viewHistory) { - navController.navigateBlockingForAsyncNavGraph( + navController.navigate( BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment( activeSessionId = customTabSessionId ) @@ -213,23 +215,20 @@ class DefaultBrowserToolbarMenuController( ), showPage = true ) - navController.navigateBlockingForAsyncNavGraph(directions) + navController.navigate(directions) } is ToolbarMenu.Item.Settings -> browserAnimator.captureEngineViewAndDrawStatically { val directions = BrowserFragmentDirections.actionBrowserFragmentToSettingsFragment() navController.nav(R.id.browserFragment, directions) } - is ToolbarMenu.Item.SyncedTabs -> browserAnimator.captureEngineViewAndDrawStatically { - navController.nav( - R.id.browserFragment, - BrowserFragmentDirections.actionBrowserFragmentToSyncedTabsFragment() - ) - } is ToolbarMenu.Item.SyncAccount -> { - val directions = if (item.signedIn) { - BrowserFragmentDirections.actionGlobalAccountSettingsFragment() - } else { - BrowserFragmentDirections.actionGlobalTurnOnSync() + val directions = when (item.accountState) { + AccountState.AUTHENTICATED -> + BrowserFragmentDirections.actionGlobalAccountSettingsFragment() + AccountState.NEEDS_REAUTHENTICATION -> + BrowserFragmentDirections.actionGlobalAccountProblemFragment() + AccountState.NO_ACCOUNT -> + BrowserFragmentDirections.actionGlobalTurnOnSync() } browserAnimator.captureEngineViewAndDrawStatically { navController.nav( @@ -298,7 +297,6 @@ class DefaultBrowserToolbarMenuController( } is ToolbarMenu.Item.FindInPage -> { findInPageLauncher() - metrics.track(Event.FindInPageOpened) } is ToolbarMenu.Item.AddonsManager -> browserAnimator.captureEngineViewAndDrawStatically { navController.nav( @@ -349,7 +347,7 @@ class DefaultBrowserToolbarMenuController( ) } is ToolbarMenu.Item.NewTab -> { - navController.navigateBlockingForAsyncNavGraph( + navController.navigate( BrowserFragmentDirections.actionGlobalHome(focusOnAddressBar = true) ) } @@ -396,7 +394,6 @@ class DefaultBrowserToolbarMenuController( is ToolbarMenu.Item.SaveToCollection -> Event.BrowserMenuItemTapped.Item.SAVE_TO_COLLECTION is ToolbarMenu.Item.AddToTopSites -> Event.BrowserMenuItemTapped.Item.ADD_TO_TOP_SITES is ToolbarMenu.Item.AddToHomeScreen -> Event.BrowserMenuItemTapped.Item.ADD_TO_HOMESCREEN - is ToolbarMenu.Item.SyncedTabs -> Event.BrowserMenuItemTapped.Item.SYNC_TABS is ToolbarMenu.Item.SyncAccount -> Event.BrowserMenuItemTapped.Item.SYNC_ACCOUNT is ToolbarMenu.Item.Bookmark -> Event.BrowserMenuItemTapped.Item.BOOKMARK is ToolbarMenu.Item.AddonsManager -> Event.BrowserMenuItemTapped.Item.ADDONS_MANAGER diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt index 154a9c63e..f7e40f050 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt @@ -23,8 +23,8 @@ import mozilla.components.browser.toolbar.BrowserToolbar import mozilla.components.browser.toolbar.behavior.BrowserToolbarBehavior import mozilla.components.browser.toolbar.display.DisplayToolbar import mozilla.components.support.utils.URLStringUtils -import mozilla.components.ui.tabcounter.TabCounterMenu import org.mozilla.fenix.R +import org.mozilla.fenix.components.toolbar.interactor.BrowserToolbarInteractor import org.mozilla.fenix.customtabs.CustomTabToolbarIntegration import org.mozilla.fenix.customtabs.CustomTabToolbarMenu import org.mozilla.fenix.ext.bookmarkStorage @@ -35,23 +35,12 @@ import org.mozilla.fenix.utils.ToolbarPopupWindow import java.lang.ref.WeakReference import mozilla.components.browser.toolbar.behavior.ToolbarPosition as MozacToolbarPosition -interface BrowserToolbarViewInteractor { - fun onBrowserToolbarPaste(text: String) - fun onBrowserToolbarPasteAndGo(text: String) - fun onBrowserToolbarClicked() - fun onBrowserToolbarMenuItemTapped(item: ToolbarMenu.Item) - fun onTabCounterClicked() - fun onTabCounterMenuItemTapped(item: TabCounterMenu.Item) - fun onScrolled(offset: Int) - fun onReaderModePressed(enabled: Boolean) -} - @ExperimentalCoroutinesApi @SuppressWarnings("LargeClass") class BrowserToolbarView( private val container: ViewGroup, private val toolbarPosition: ToolbarPosition, - private val interactor: BrowserToolbarViewInteractor, + private val interactor: BrowserToolbarInteractor, private val customTabSession: CustomTabSessionState?, private val lifecycleOwner: LifecycleOwner ) : LayoutContainer { @@ -79,7 +68,7 @@ class BrowserToolbarView( @VisibleForTesting internal val isPwaTabOrTwaTab: Boolean get() = customTabSession?.config?.externalAppType == ExternalAppType.PROGRESSIVE_WEB_APP || - customTabSession?.config?.externalAppType == ExternalAppType.TRUSTED_WEB_ACTIVITY + customTabSession?.config?.externalAppType == ExternalAppType.TRUSTED_WEB_ACTIVITY init { val isCustomTabSession = customTabSession != null @@ -101,7 +90,6 @@ class BrowserToolbarView( setToolbarBehavior() elevation = resources.getDimension(R.dimen.browser_fragment_toolbar_elevation) - if (!isCustomTabSession) { display.setUrlBackground(getDrawable(R.drawable.search_url_background)) } 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 0cf53913a..f39d95d1b 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 @@ -34,15 +34,10 @@ import mozilla.components.feature.webcompat.reporter.WebCompatReporterFeature import mozilla.components.lib.state.ext.flowScoped import mozilla.components.support.ktx.android.content.getColorFromAttr import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged -import org.mozilla.fenix.FeatureFlags -import org.mozilla.fenix.FeatureFlags.tabsTrayRewrite -import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R -import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.components.accounts.FenixAccountManager import org.mozilla.fenix.experiments.ExperimentBranch import org.mozilla.fenix.experiments.FeatureId -import org.mozilla.fenix.ext.asActivity import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.withExperiment @@ -82,12 +77,7 @@ open class DefaultToolbarMenu( override val menuBuilder by lazy { WebExtensionBrowserMenuBuilder( - items = - if (FeatureFlags.toolbarMenuFeature) { - newCoreMenuItems - } else { - oldCoreMenuItems - }, + items = coreMenuItems, endOfMenuAlwaysVisible = shouldUseBottomToolbar, store = store, style = WebExtensionBrowserMenuBuilder.Style( @@ -161,39 +151,19 @@ open class DefaultToolbarMenu( registerForIsBookmarkedUpdates() - if (FeatureFlags.toolbarMenuFeature) { - BrowserMenuItemToolbar(listOf(back, forward, share, refresh), isSticky = true) - } else { - val bookmark = BrowserMenuItemToolbar.TwoStateButton( - primaryImageResource = R.drawable.ic_bookmark_filled, - primaryContentDescription = context.getString(R.string.browser_menu_edit_bookmark), - primaryImageTintResource = primaryTextColor(), - // TwoStateButton.isInPrimaryState must be synchronous, and checking bookmark state is - // relatively slow. The best we can do here is periodically compute and cache a new "is - // bookmarked" state, and use that whenever the menu has been opened. - isInPrimaryState = { isCurrentUrlBookmarked }, - secondaryImageResource = R.drawable.ic_bookmark_outline, - secondaryContentDescription = context.getString(R.string.browser_menu_bookmark), - secondaryImageTintResource = primaryTextColor(), - disableInSecondaryState = false - ) { - handleBookmarkItemTapped() - } - - BrowserMenuItemToolbar(listOf(back, forward, bookmark, share, refresh)) - } + BrowserMenuItemToolbar(listOf(back, forward, share, refresh), isSticky = true) } // Predicates that need to be repeatedly called as the session changes @VisibleForTesting(otherwise = PRIVATE) fun canAddToHomescreen(): Boolean = selectedSession != null && isPinningSupported && - !context.components.useCases.webAppUseCases.isInstallable() + !context.components.useCases.webAppUseCases.isInstallable() @VisibleForTesting(otherwise = PRIVATE) fun canInstall(): Boolean = selectedSession != null && isPinningSupported && - context.components.useCases.webAppUseCases.isInstallable() + context.components.useCases.webAppUseCases.isInstallable() @VisibleForTesting(otherwise = PRIVATE) fun shouldShowOpenInApp(): Boolean = selectedSession?.let { session -> @@ -222,170 +192,6 @@ open class DefaultToolbarMenu( onItemTapped.invoke(ToolbarMenu.Item.InstallPwaToHomeScreen) } - private val oldCoreMenuItems by lazy { - val settings = BrowserMenuHighlightableItem( - label = context.getString(R.string.browser_menu_settings), - startImageResource = R.drawable.ic_settings, - iconTintColorResource = if (hasAccountProblem) - ThemeManager.resolveAttribute(R.attr.syncDisconnected, context) else - primaryTextColor(), - textColorResource = if (hasAccountProblem) - ThemeManager.resolveAttribute(R.attr.primaryText, context) else - primaryTextColor(), - highlight = BrowserMenuHighlight.HighPriority( - endImageResource = R.drawable.ic_sync_disconnected, - backgroundTint = context.getColorFromAttr(R.attr.syncDisconnectedBackground), - canPropagate = false - ), - isHighlighted = { hasAccountProblem } - ) { - onItemTapped.invoke(ToolbarMenu.Item.Settings) - } - - val desktopMode = BrowserMenuImageSwitch( - imageResource = R.drawable.ic_desktop, - label = context.getString(R.string.browser_menu_desktop_site), - initialState = { - selectedSession?.content?.desktopMode ?: false - } - ) { checked -> - onItemTapped.invoke(ToolbarMenu.Item.RequestDesktop(checked)) - } - - val addToTopSites = BrowserMenuImageText( - label = context.getString(R.string.browser_menu_add_to_top_sites), - imageResource = R.drawable.ic_top_sites, - iconTintColorResource = primaryTextColor() - ) { - onItemTapped.invoke(ToolbarMenu.Item.AddToTopSites) - } - - val addToHomescreen = BrowserMenuImageText( - label = context.getString(R.string.browser_menu_add_to_homescreen), - imageResource = R.drawable.ic_add_to_homescreen, - iconTintColorResource = primaryTextColor() - ) { - onItemTapped.invoke(ToolbarMenu.Item.AddToHomeScreen) - } - - val syncedTabs = BrowserMenuImageText( - label = context.getString(R.string.synced_tabs), - imageResource = R.drawable.ic_synced_tabs, - iconTintColorResource = primaryTextColor() - ) { - onItemTapped.invoke(ToolbarMenu.Item.SyncedTabs) - } - - val findInPage = BrowserMenuImageText( - label = context.getString(R.string.browser_menu_find_in_page), - imageResource = R.drawable.mozac_ic_search, - iconTintColorResource = primaryTextColor() - ) { - onItemTapped.invoke(ToolbarMenu.Item.FindInPage) - } - - val reportSiteIssuePlaceholder = WebExtensionPlaceholderMenuItem( - id = WebCompatReporterFeature.WEBCOMPAT_REPORTER_EXTENSION_ID - ) - - val saveToCollection = BrowserMenuImageText( - label = context.getString(R.string.browser_menu_save_to_collection_2), - imageResource = R.drawable.ic_tab_collection, - iconTintColorResource = primaryTextColor() - ) { - onItemTapped.invoke(ToolbarMenu.Item.SaveToCollection) - } - - val deleteDataOnQuit = BrowserMenuImageText( - label = context.getString(R.string.delete_browsing_data_on_quit_action), - imageResource = R.drawable.ic_exit, - iconTintColorResource = primaryTextColor() - ) { - onItemTapped.invoke(ToolbarMenu.Item.Quit) - } - - val readerAppearance = BrowserMenuImageText( - label = context.getString(R.string.browser_menu_read_appearance), - imageResource = R.drawable.ic_readermode_appearance, - iconTintColorResource = primaryTextColor() - ) { - onItemTapped.invoke(ToolbarMenu.Item.CustomizeReaderView) - } - - val openInApp = BrowserMenuHighlightableItem( - label = context.getString(R.string.browser_menu_open_app_link), - startImageResource = R.drawable.ic_open_in_app, - iconTintColorResource = primaryTextColor(), - highlight = BrowserMenuHighlight.LowPriority( - label = context.getString(R.string.browser_menu_open_app_link), - notificationTint = getColor(context, R.color.whats_new_notification_color) - ), - isHighlighted = { !context.settings().openInAppOpened } - ) { - onItemTapped.invoke(ToolbarMenu.Item.OpenInApp) - } - - val historyItem = BrowserMenuImageText( - context.getString(R.string.library_history), - R.drawable.ic_history, - primaryTextColor() - ) { - onItemTapped.invoke(ToolbarMenu.Item.History) - } - - val bookmarksItem = BrowserMenuImageText( - context.getString(R.string.library_bookmarks), - R.drawable.ic_bookmark_filled, - primaryTextColor() - ) { - onItemTapped.invoke(ToolbarMenu.Item.Bookmarks) - } - - val downloadsItem = BrowserMenuImageText( - context.getString(R.string.library_downloads), - R.drawable.ic_download, - primaryTextColor() - ) { - onItemTapped.invoke(ToolbarMenu.Item.Downloads) - } - - // Predicates that are called once, during screen init - val shouldShowSaveToCollection = (context.asActivity() as? HomeActivity) - ?.browsingModeManager?.mode == BrowsingMode.Normal - val shouldDeleteDataOnQuit = context.components.settings - .shouldDeleteBrowsingDataOnQuit - - val menuItems = listOfNotNull( - downloadsItem, - historyItem, - bookmarksItem, - syncedTabs, - settings, - if (shouldDeleteDataOnQuit) deleteDataOnQuit else null, - BrowserMenuDivider(), - reportSiteIssuePlaceholder, - findInPage, - getSetDefaultBrowserItem()?.let { BrowserMenuDivider() }, - getSetDefaultBrowserItem(), - getSetDefaultBrowserItem()?.let { BrowserMenuDivider() }, - addToTopSites, - addToHomescreen.apply { visible = ::canAddToHomescreen }, - installToHomescreen.apply { visible = ::canInstall }, - if (shouldShowSaveToCollection) saveToCollection else null, - desktopMode, - openInApp.apply { visible = ::shouldShowOpenInApp }, - readerAppearance.apply { visible = ::shouldShowReaderViewCustomization }, - BrowserMenuDivider(), - menuToolbar - ) - - if (shouldUseBottomToolbar) { - menuItems - } else { - menuItems.reversed() - } - } - val newTabItem = BrowserMenuImageText( context.getString(R.string.library_new_tab), R.drawable.ic_new, @@ -454,7 +260,8 @@ open class DefaultToolbarMenu( } val reportSiteIssuePlaceholder = WebExtensionPlaceholderMenuItem( - id = WebCompatReporterFeature.WEBCOMPAT_REPORTER_EXTENSION_ID + id = WebCompatReporterFeature.WEBCOMPAT_REPORTER_EXTENSION_ID, + iconTintColorResource = primaryTextColor() ) val addToHomeScreenItem = BrowserMenuImageText( @@ -526,35 +333,21 @@ open class DefaultToolbarMenu( onItemTapped.invoke(ToolbarMenu.Item.Quit) } - val syncedTabsItem = BrowserMenuImageText( - context.getString(R.string.synced_tabs), - R.drawable.ic_synced_tabs, - primaryTextColor() - ) { - onItemTapped.invoke(ToolbarMenu.Item.SyncedTabs) - } - - private fun getSyncItemTitle(): String { - val authenticatedAccount = accountManager.authenticatedAccount - val email = accountManager.accountProfileEmail - - return if (authenticatedAccount && !email.isNullOrEmpty()) { - email - } else { - context.getString(R.string.sync_menu_sign_in) - } - } + private fun getSyncItemTitle() = + accountManager.accountProfileEmail ?: context.getString(R.string.sync_menu_sign_in) val syncMenuItem = BrowserMenuImageText( getSyncItemTitle(), R.drawable.ic_signed_out, primaryTextColor() ) { - onItemTapped.invoke(ToolbarMenu.Item.SyncAccount(accountManager.signedInToFxa())) + onItemTapped.invoke( + ToolbarMenu.Item.SyncAccount(accountManager.accountState) + ) } @VisibleForTesting(otherwise = PRIVATE) - val newCoreMenuItems by lazy { + val coreMenuItems by lazy { val menuItems = listOfNotNull( if (shouldUseBottomToolbar) null else menuToolbar, @@ -564,7 +357,7 @@ open class DefaultToolbarMenu( historyItem, downloadsItem, extensionsItem, - if (tabsTrayRewrite) syncMenuItem else syncedTabsItem, + syncMenuItem, BrowserMenuDivider(), getSetDefaultBrowserItem(), getSetDefaultBrowserItem()?.let { BrowserMenuDivider() }, diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt index aaa867144..5a248bf12 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarIntegration.kt @@ -25,6 +25,7 @@ import mozilla.components.feature.toolbar.ToolbarPresenter import mozilla.components.support.base.feature.LifecycleAwareFeature import mozilla.components.support.ktx.android.view.hideKeyboard import org.mozilla.fenix.R +import org.mozilla.fenix.components.toolbar.interactor.BrowserToolbarInteractor import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.theme.ThemeManager @@ -88,7 +89,7 @@ class DefaultToolbarIntegration( lifecycleOwner: LifecycleOwner, sessionId: String? = null, isPrivate: Boolean, - interactor: BrowserToolbarViewInteractor, + interactor: BrowserToolbarInteractor, engine: Engine ) : ToolbarIntegration( context = context, @@ -133,7 +134,7 @@ class DefaultToolbarIntegration( DisplayToolbar.Indicators.HIGHLIGHT ) } - context.settings().shouldUseTrackingProtection + context.settings().shouldUseTrackingProtection toolbar.display.icons = toolbar.display.icons.copy( emptyIcon = null, @@ -154,11 +155,11 @@ class DefaultToolbarIntegration( interactor.onTabCounterMenuItemTapped(it) }, iconColor = - if (isPrivate) { - ContextCompat.getColor(context, R.color.primary_text_private_theme) - } else { - null - } + if (isPrivate) { + ContextCompat.getColor(context, R.color.primary_text_private_theme) + } else { + null + } ).also { it.updateMenu(context.settings().toolbarPosition) } 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 ec384c188..8477905b9 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 @@ -6,6 +6,7 @@ package org.mozilla.fenix.components.toolbar import mozilla.components.browser.menu.BrowserMenuBuilder import mozilla.components.browser.menu.item.BrowserMenuItemToolbar +import org.mozilla.fenix.components.accounts.AccountState interface ToolbarMenu { sealed class Item { @@ -22,8 +23,7 @@ interface ToolbarMenu { object AddToTopSites : Item() object InstallPwaToHomeScreen : Item() object AddToHomeScreen : Item() - object SyncedTabs : Item() - data class SyncAccount(val signedIn: Boolean) : Item() + data class SyncAccount(val accountState: AccountState) : Item() object AddonsManager : Item() object Quit : Item() object OpenInApp : Item() diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/interactor/BrowserToolbarInteractor.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/interactor/BrowserToolbarInteractor.kt new file mode 100644 index 000000000..0b636726e --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/interactor/BrowserToolbarInteractor.kt @@ -0,0 +1,80 @@ +/* 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.toolbar.interactor + +import mozilla.components.ui.tabcounter.TabCounterMenu +import org.mozilla.fenix.components.toolbar.BrowserToolbarController +import org.mozilla.fenix.components.toolbar.BrowserToolbarMenuController +import org.mozilla.fenix.components.toolbar.ToolbarMenu + +/** + * Interface for the browser toolbar interactor. This interface is implemented by objects that + * want to respond to user interaction on the browser toolbar. + */ +interface BrowserToolbarInteractor { + fun onBrowserToolbarPaste(text: String) + fun onBrowserToolbarPasteAndGo(text: String) + fun onBrowserToolbarClicked() + fun onBrowserToolbarMenuItemTapped(item: ToolbarMenu.Item) + fun onTabCounterClicked() + fun onTabCounterMenuItemTapped(item: TabCounterMenu.Item) + fun onScrolled(offset: Int) + fun onReaderModePressed(enabled: Boolean) + + /** + * Navigates to the Home screen. Called when a user taps on the Home button. + */ + fun onHomeButtonClicked() +} + +/** + * The default implementation of [BrowserToolbarInteractor]. + * + * @property browserToolbarController [BrowserToolbarController] to which user actions can be + * delegated for all interactions on the browser toolbar. + * @property menuController [BrowserToolbarMenuController] to which user actions can be delegated + * for all interactions on the the browser toolbar menu. + */ +class DefaultBrowserToolbarInteractor( + private val browserToolbarController: BrowserToolbarController, + private val menuController: BrowserToolbarMenuController +) : BrowserToolbarInteractor { + + override fun onTabCounterClicked() { + browserToolbarController.handleTabCounterClick() + } + + override fun onTabCounterMenuItemTapped(item: TabCounterMenu.Item) { + browserToolbarController.handleTabCounterItemInteraction(item) + } + + override fun onBrowserToolbarPaste(text: String) { + browserToolbarController.handleToolbarPaste(text) + } + + override fun onBrowserToolbarPasteAndGo(text: String) { + browserToolbarController.handleToolbarPasteAndGo(text) + } + + override fun onBrowserToolbarClicked() { + browserToolbarController.handleToolbarClick() + } + + override fun onBrowserToolbarMenuItemTapped(item: ToolbarMenu.Item) { + menuController.handleToolbarItemInteraction(item) + } + + override fun onScrolled(offset: Int) { + browserToolbarController.handleScroll(offset) + } + + override fun onReaderModePressed(enabled: Boolean) { + browserToolbarController.handleReaderModePressed(enabled) + } + + override fun onHomeButtonClicked() { + browserToolbarController.handleHomeButtonClick() + } +} diff --git a/app/src/main/java/org/mozilla/fenix/crashes/CrashReporterController.kt b/app/src/main/java/org/mozilla/fenix/crashes/CrashReporterController.kt index 4183a4bfd..d4cf9b68a 100644 --- a/app/src/main/java/org/mozilla/fenix/crashes/CrashReporterController.kt +++ b/app/src/main/java/org/mozilla/fenix/crashes/CrashReporterController.kt @@ -5,6 +5,7 @@ package org.mozilla.fenix.crashes import androidx.navigation.NavController +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job @@ -69,6 +70,7 @@ class CrashReporterController( * @param sendCrash If true, submit a crash report. * @return Job if report is submitted through an IO thread, null otherwise */ + @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage private fun submitReportIfNecessary(sendCrash: Boolean): Job? { var job: Job? = null val didSubmitReport = if (sendCrash && settings.isCrashReportingEnabled) { diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabToolbarMenu.kt b/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabToolbarMenu.kt index f82398c6a..222ff8684 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabToolbarMenu.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabToolbarMenu.kt @@ -170,7 +170,7 @@ class CustomTabToolbarMenu( private val poweredBy = BrowserMenuCategory( label = context.getStringWithArgSafe(R.string.browser_menu_powered_by, appName) - .toUpperCase(Locale.getDefault()), + .uppercase(Locale.getDefault()), textSize = CAPTION_TEXT_SIZE, textColorResource = primaryTextColor(), textStyle = Typeface.NORMAL 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 a7546b378..a8331af69 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt @@ -48,23 +48,8 @@ open class ExternalAppBrowserActivity : HomeActivity() { final override fun getIntentSource(intent: SafeIntent) = Event.OpenedApp.Source.CUSTOM_TAB - final override fun getIntentAllSource(intent: SafeIntent) = Event.AppReceivedIntent.Source.CUSTOM_TAB - final override fun getIntentSessionId(intent: SafeIntent) = intent.getSessionId() - override fun startupTelemetryOnCreateCalled(safeIntent: SafeIntent, hasSavedInstanceState: Boolean) { - components.appStartupTelemetry.onExternalAppBrowserOnCreate( - safeIntent, - hasSavedInstanceState, - // HomeActivity is init before ExternalAppBrowserActivity so we use that time. - homeActivityInitTimeStampNanoSeconds, - rootContainer - ) - - // coldStartupDurationTelemetry.onHomeActivityOnCreate is intentionally omitted so we don't - // include even more unpredictable code paths in the results. - } - override fun navigateToBrowserOnColdStart() { // No-op for external app } diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt index dd6eeda75..a1d88d9c6 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt @@ -23,7 +23,7 @@ import mozilla.components.feature.pwa.feature.ManifestUpdateFeature import mozilla.components.feature.pwa.feature.WebAppActivityFeature import mozilla.components.feature.pwa.feature.WebAppHideToolbarFeature import mozilla.components.feature.pwa.feature.WebAppSiteControlsFeature -import mozilla.components.feature.sitepermissions.SitePermissions +import mozilla.components.concept.engine.permission.SitePermissions import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.ktx.android.arch.lifecycle.addObservers @@ -37,6 +37,7 @@ import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.ext.runIfFragmentIsAttached /** * Fragment used for browsing the web within external apps. @@ -66,7 +67,7 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler toolbar = toolbar, sessionId = customTabSessionId, activity = activity, - onItemTapped = { browserInteractor.onBrowserToolbarMenuItemTapped(it) }, + onItemTapped = { browserToolbarInteractor.onBrowserToolbarMenuItemTapped(it) }, isPrivate = tab.content.private, shouldReverseItems = !activity.settings().shouldUseBottomToolbar ), @@ -193,16 +194,18 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler override fun navToTrackingProtectionPanel(tab: SessionState) { requireComponents.useCases.trackingProtectionUseCases.containsException(tab.id) { contains -> - val isEnabled = tab.trackingProtection.enabled && !contains - val directions = - ExternalAppBrowserFragmentDirections - .actionGlobalTrackingProtectionPanelDialogFragment( - sessionId = tab.id, - url = tab.content.url, - trackingProtectionEnabled = isEnabled, - gravity = getAppropriateLayoutGravity() - ) - nav(R.id.externalAppBrowserFragment, directions) + runIfFragmentIsAttached { + val isEnabled = tab.trackingProtection.enabled && !contains + val directions = + ExternalAppBrowserFragmentDirections + .actionGlobalTrackingProtectionPanelDialogFragment( + sessionId = tab.id, + url = tab.content.url, + trackingProtectionEnabled = isEnabled, + gravity = getAppropriateLayoutGravity() + ) + nav(R.id.externalAppBrowserFragment, directions) + } } } diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/FennecWebAppIntentProcessor.kt b/app/src/main/java/org/mozilla/fenix/customtabs/FennecWebAppIntentProcessor.kt index 68fbfe249..5373a70ca 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/FennecWebAppIntentProcessor.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/FennecWebAppIntentProcessor.kt @@ -11,7 +11,6 @@ import androidx.annotation.VisibleForTesting import androidx.core.content.ContextCompat import mozilla.components.browser.state.state.CustomTabConfig import mozilla.components.browser.state.state.SessionState -import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.manifest.WebAppManifest import mozilla.components.concept.engine.manifest.WebAppManifestParser import mozilla.components.concept.engine.manifest.getOrNull @@ -20,7 +19,7 @@ import mozilla.components.feature.intent.processing.IntentProcessor import mozilla.components.feature.pwa.ManifestStorage import mozilla.components.feature.pwa.ext.putWebAppManifest import mozilla.components.feature.pwa.ext.toCustomTabConfig -import mozilla.components.feature.tabs.TabsUseCases +import mozilla.components.feature.tabs.CustomTabsUseCases import mozilla.components.support.base.log.logger.Logger import mozilla.components.support.utils.SafeIntent import mozilla.components.support.utils.toSafeIntent @@ -36,7 +35,7 @@ import java.io.IOException */ class FennecWebAppIntentProcessor( private val context: Context, - private val addNewTabUseCase: TabsUseCases.AddNewTabUseCase, + private val useCases: CustomTabsUseCases, private val storage: ManifestStorage ) : IntentProcessor { val logger = Logger("FennecWebAppIntentProcessor") @@ -59,13 +58,20 @@ class FennecWebAppIntentProcessor( return if (!url.isNullOrEmpty() && matches(intent)) { val webAppManifest = runBlockingIncrement { loadManifest(safeIntent, url) } - val sessionId = addNewTabUseCase( - url = url, - source = SessionState.Source.HOME_SCREEN, - flags = EngineSession.LoadUrlFlags.external(), - webAppManifest = webAppManifest, - customTabConfig = webAppManifest?.toCustomTabConfig() ?: createFallbackCustomTabConfig() - ) + val sessionId = if (webAppManifest != null) { + useCases.addWebApp( + url = url, + source = SessionState.Source.Internal.HomeScreen, + webAppManifest = webAppManifest, + customTabConfig = webAppManifest.toCustomTabConfig() + ) + } else { + useCases.add( + url = url, + source = SessionState.Source.Internal.HomeScreen, + customTabConfig = createFallbackCustomTabConfig() + ) + } intent.putSessionId(sessionId) if (webAppManifest != null) { diff --git a/app/src/main/java/org/mozilla/fenix/downloads/DynamicDownloadDialog.kt b/app/src/main/java/org/mozilla/fenix/downloads/DynamicDownloadDialog.kt index df3fd5513..3b2b2b3b3 100644 --- a/app/src/main/java/org/mozilla/fenix/downloads/DynamicDownloadDialog.kt +++ b/app/src/main/java/org/mozilla/fenix/downloads/DynamicDownloadDialog.kt @@ -15,9 +15,6 @@ import mozilla.components.browser.state.state.content.DownloadState import mozilla.components.feature.downloads.AbstractFetchDownloadService import mozilla.components.feature.downloads.toMegabyteOrKilobyteString import org.mozilla.fenix.R -import org.mozilla.fenix.components.metrics.Event -import org.mozilla.fenix.components.metrics.MetricController -import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.settings /** @@ -29,7 +26,6 @@ import org.mozilla.fenix.ext.settings class DynamicDownloadDialog( private val container: ViewGroup, private val downloadState: DownloadState?, - private val metrics: MetricController, private val didFail: Boolean, private val tryAgain: (String) -> Unit, private val onCannotOpenFile: (DownloadState) -> Unit, @@ -83,7 +79,6 @@ class DynamicDownloadDialog( ) setOnClickListener { tryAgain(downloadState.id) - context.metrics.track(Event.InAppNotificationDownloadTryAgain) dismiss(view) } } @@ -103,8 +98,6 @@ class DynamicDownloadDialog( mozilla.components.feature.downloads.R.string.mozac_feature_downloads_button_open ) setOnClickListener { - metrics.track(Event.DownloadsItemOpened) - val fileWasOpened = AbstractFetchDownloadService.openFile( applicationContext = context.applicationContext, download = downloadState @@ -114,7 +107,6 @@ class DynamicDownloadDialog( onCannotOpenFile(downloadState) } - context.metrics.track(Event.InAppNotificationDownloadOpen) dismiss(view) } } diff --git a/app/src/main/java/org/mozilla/fenix/exceptions/login/LoginExceptionsFragment.kt b/app/src/main/java/org/mozilla/fenix/exceptions/login/LoginExceptionsFragment.kt index a10a06a44..b9f6c19df 100644 --- a/app/src/main/java/org/mozilla/fenix/exceptions/login/LoginExceptionsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/exceptions/login/LoginExceptionsFragment.kt @@ -11,12 +11,10 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.asLiveData import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.observe import kotlinx.android.synthetic.main.fragment_exceptions.view.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.plus -import mozilla.components.feature.logins.exceptions.LoginException import mozilla.components.lib.state.ext.consumeFrom import org.mozilla.fenix.R import org.mozilla.fenix.components.StoreProvider @@ -62,7 +60,7 @@ class LoginExceptionsFragment : Fragment() { private fun subscribeToLoginExceptions() { requireComponents.core.loginExceptionStorage.getLoginExceptions().asLiveData() - .observe>(viewLifecycleOwner) { exceptions -> + .observe(viewLifecycleOwner) { exceptions -> exceptionsStore.dispatch(ExceptionsFragmentAction.Change(exceptions)) } } diff --git a/app/src/main/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsHeaderViewHolder.kt b/app/src/main/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsHeaderViewHolder.kt index 97374247c..de05538da 100644 --- a/app/src/main/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsHeaderViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/exceptions/viewholders/ExceptionsHeaderViewHolder.kt @@ -5,9 +5,9 @@ package org.mozilla.fenix.exceptions.viewholders import android.view.View +import android.widget.TextView import androidx.annotation.StringRes import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.exceptions_description.view.* import org.mozilla.fenix.R class ExceptionsHeaderViewHolder( @@ -16,7 +16,8 @@ class ExceptionsHeaderViewHolder( ) : RecyclerView.ViewHolder(view) { init { - view.exceptions_description.text = view.context.getString(description) + view.findViewById(R.id.exceptions_description).text = + view.context.getString(description) } companion object { diff --git a/app/src/main/java/org/mozilla/fenix/experiments/NimbusSetup.kt b/app/src/main/java/org/mozilla/fenix/experiments/NimbusSetup.kt index 9933439e7..2919ddadc 100644 --- a/app/src/main/java/org/mozilla/fenix/experiments/NimbusSetup.kt +++ b/app/src/main/java/org/mozilla/fenix/experiments/NimbusSetup.kt @@ -7,26 +7,38 @@ package org.mozilla.fenix.experiments import android.content.Context import android.net.Uri import android.os.StrictMode -import io.sentry.Sentry import mozilla.components.service.nimbus.NimbusApi import mozilla.components.service.nimbus.Nimbus import mozilla.components.service.nimbus.NimbusAppInfo import mozilla.components.service.nimbus.NimbusDisabled import mozilla.components.service.nimbus.NimbusServerSettings import mozilla.components.support.base.log.logger.Logger +import org.mozilla.experiments.nimbus.internal.NimbusErrorException import org.mozilla.fenix.BuildConfig import org.mozilla.fenix.R -import org.mozilla.fenix.components.isSentryEnabled import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings @Suppress("TooGenericExceptionCaught") -fun createNimbus(context: Context, url: String?): NimbusApi = - try { +fun createNimbus(context: Context, url: String?): NimbusApi { + val errorReporter: ((String, Throwable) -> Unit) = reporter@{ message, e -> + Logger.error("Nimbus error: $message", e) + + if (e is NimbusErrorException && !e.isReportableError()) { + return@reporter + } + + context.components.analytics.crashReporter.submitCaughtException(e) + } + return try { // Eventually we'll want to use `NimbusDisabled` when we have no NIMBUS_ENDPOINT. // but we keep this here to not mix feature flags and how we configure Nimbus. val serverSettings = if (!url.isNullOrBlank()) { - NimbusServerSettings(url = Uri.parse(url)) + if (context.settings().nimbusUsePreview) { + NimbusServerSettings(url = Uri.parse(url), collection = "nimbus-preview") + } else { + NimbusServerSettings(url = Uri.parse(url)) + } } else { null } @@ -53,7 +65,7 @@ fun createNimbus(context: Context, url: String?): NimbusApi = // and would mostly produce the value `Beta` and rarely would produce `beta`. channel = BuildConfig.BUILD_TYPE ) - Nimbus(context, appInfo, serverSettings).apply { + Nimbus(context, appInfo, serverSettings, errorReporter).apply { // This performs the minimal amount of work required to load branch and enrolment data // into memory. If `getExperimentBranch` is called from another thread between here // and the next nimbus disk write (setting `globalUserParticipation` or @@ -83,10 +95,21 @@ fun createNimbus(context: Context, url: String?): NimbusApi = } catch (e: Throwable) { // Something went wrong. We'd like not to, but stability of the app is more important than // failing fast here. - if (isSentryEnabled()) { - Sentry.capture(e) - } else { - Logger.error("Failed to initialize Nimbus", e) - } + errorReporter("Failed to initialize Nimbus", e) NimbusDisabled() } +} + +/** + * Classifies which errors we should forward to our crash reporter or not. We want to filter out the + * non-reportable ones if we know there is no reasonable action that we can perform. + * + * This fix should be upstreamed as part of: https://github.com/mozilla/application-services/issues/4333 + */ +fun NimbusErrorException.isReportableError(): Boolean { + return when (this) { + is NimbusErrorException.RequestError, + is NimbusErrorException.ResponseError -> false + else -> true + } +} 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 f38afd406..d142aee6e 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/Activity.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/Activity.kt @@ -37,11 +37,13 @@ fun Activity.enterToImmersiveMode() { window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) // This will be addressed on https://github.com/mozilla-mobile/fenix/issues/17804 @Suppress("DEPRECATION") - window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.decorView.systemUiVisibility = ( + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN - or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) + or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + ) } fun Activity.breadcrumb( diff --git a/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt new file mode 100644 index 000000000..a76b802b3 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/ext/BrowserState.kt @@ -0,0 +1,44 @@ +/* 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.selector.normalTabs +import mozilla.components.browser.state.selector.selectedNormalTab +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.feature.tabs.ext.hasMediaPlayed + +/** + * Get the last opened normal tab and the last tab with in progress media, if available. + * + * @return A list of the last opened tab and the last tab with in progress media + * if distinct and available or an empty list. + */ +fun BrowserState.asRecentTabs(): List { + return mutableListOf().apply { + val lastOpenedNormalTab = lastOpenedNormalTab + lastOpenedNormalTab?.let { add(it) } + inProgressMediaTab + ?.takeUnless { it == lastOpenedNormalTab } + ?.let { + add(it) + } + } +} + +/** + * Get the selected normal tab or the last accessed normal tab + * if there is no selected tab or the selected tab is a private one. + */ +val BrowserState.lastOpenedNormalTab: TabSessionState? + get() = selectedNormalTab ?: normalTabs.maxByOrNull { it.lastAccess } + +/** + * Get the last tab with in progress media. + */ +val BrowserState.inProgressMediaTab: TabSessionState? + get() = normalTabs + .filter { it.hasMediaPlayed() } + .maxByOrNull { it.lastMediaAccessState.lastMediaAccess } diff --git a/app/src/main/java/org/mozilla/fenix/ext/ConnectivityManager.kt b/app/src/main/java/org/mozilla/fenix/ext/ConnectivityManager.kt index 465090c35..7b60ad21a 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/ConnectivityManager.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/ConnectivityManager.kt @@ -18,7 +18,7 @@ fun ConnectivityManager.isOnline(network: Network? = null): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { getNetworkCapabilities(network ?: activeNetwork)?.let { it.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && - it.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + it.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) } ?: false } else { // for devices below android M, there's no better way to get this. diff --git a/app/src/main/java/org/mozilla/fenix/ext/NavController.kt b/app/src/main/java/org/mozilla/fenix/ext/NavController.kt index 8ba9cebfd..8ee815f14 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/NavController.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/NavController.kt @@ -2,63 +2,35 @@ * 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/. */ -// We suppress the calls to `navigate` since we invoke the Android `NavController.navigate` through -// this file. Detekt checks for the `navigate()` function calls, which should be ignored in this file. -@file:Suppress("MozillaNavigateCheck") package org.mozilla.fenix.ext import androidx.annotation.IdRes import androidx.navigation.NavController import androidx.navigation.NavDirections import androidx.navigation.NavOptions -import io.sentry.Sentry -import org.mozilla.fenix.components.isSentryEnabled -import org.mozilla.fenix.perf.NavGraphProvider +import mozilla.components.support.base.log.logger.Logger /** * Navigate from the fragment with [id] using the given [directions]. * If the id doesn't match the current destination, an error is recorded. */ fun NavController.nav(@IdRes id: Int?, directions: NavDirections, navOptions: NavOptions? = null) { - NavGraphProvider.blockForNavGraphInflation(this) - if (id == null || this.currentDestination?.id == id) { this.navigate(directions, navOptions) } else { - recordIdException(this.currentDestination?.id, id) + Logger.error("Fragment id ${this.currentDestination?.id} did not match expected $id") } } -fun NavController.navigateBlockingForAsyncNavGraph(resId: Int) { - NavGraphProvider.blockForNavGraphInflation(this) - this.navigate(resId) -} - -fun NavController.navigateBlockingForAsyncNavGraph(directions: NavDirections) { - NavGraphProvider.blockForNavGraphInflation(this) - this.navigate(directions) -} - -fun NavController.navigateBlockingForAsyncNavGraph(directions: NavDirections, navOptions: NavOptions?) { - NavGraphProvider.blockForNavGraphInflation(this) - this.navigate(directions, navOptions) -} - fun NavController.alreadyOnDestination(@IdRes destId: Int?): Boolean { return destId?.let { currentDestination?.id == it || popBackStack(it, false) } ?: false } -fun recordIdException(actual: Int?, expected: Int?) { - if (isSentryEnabled()) { - Sentry.capture("Fragment id $actual did not match expected $expected") - } -} - fun NavController.navigateSafe( @IdRes resId: Int, directions: NavDirections ) { if (currentDestination?.id == resId) { - this.navigateBlockingForAsyncNavGraph(directions) + this.navigate(directions) } } diff --git a/app/src/main/java/org/mozilla/fenix/ext/SessionManager.kt b/app/src/main/java/org/mozilla/fenix/ext/SessionManager.kt deleted file mode 100644 index ba25e340d..000000000 --- a/app/src/main/java/org/mozilla/fenix/ext/SessionManager.kt +++ /dev/null @@ -1,22 +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 mozilla.components.browser.session.SessionManager - -/** - * Returns only normal or only private sessions. - */ -fun SessionManager.sessionsOfType(private: Boolean) = - sessions.asSequence().filter { it.private == private } - -/** - * @return the number of currently active sessions that are neither custom nor private - */ -fun SessionManager.normalSessionSize(): Int { - return this.sessions.filter { session -> - (!session.isCustomTabSession() && !session.private) - }.size -} diff --git a/app/src/main/java/org/mozilla/fenix/ext/String.kt b/app/src/main/java/org/mozilla/fenix/ext/String.kt index 19c84249e..cbbd5705d 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/String.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/String.kt @@ -75,7 +75,7 @@ fun String.toShortUrl(publicSuffixList: PublicSuffixList): String { return inputString .stripUserInfo() - .toLowerCase(Locale.getDefault()) + .lowercase(Locale.getDefault()) .stripPrefixes() .toUnicode() } diff --git a/app/src/main/java/org/mozilla/fenix/ext/TabCollection.kt b/app/src/main/java/org/mozilla/fenix/ext/TabCollection.kt index 71de2d2e4..0e89cde37 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/TabCollection.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/TabCollection.kt @@ -31,9 +31,15 @@ fun TabCollection.getIconColor(context: Context): Int { * Then get the numbers from all these default names, compute the maximum number and add one. */ fun List.getDefaultCollectionNumber(): Int { - return (this - .map { it.title } - .filter { it.matches(Regex("Collection\\s\\d+")) } - .map { Integer.valueOf(it.split(" ")[DefaultCollectionCreationController.DEFAULT_COLLECTION_NUMBER_POSITION]) } - .maxOrNull() ?: 0) + DefaultCollectionCreationController.DEFAULT_INCREMENT_VALUE + return ( + this + .map { it.title } + .filter { it.matches(Regex("Collection\\s\\d+")) } + .map { + Integer.valueOf( + it.split(" ")[DefaultCollectionCreationController.DEFAULT_COLLECTION_NUMBER_POSITION] + ) + } + .maxOrNull() ?: 0 + ) + DefaultCollectionCreationController.DEFAULT_INCREMENT_VALUE } diff --git a/app/src/main/java/org/mozilla/fenix/ext/View.kt b/app/src/main/java/org/mozilla/fenix/ext/View.kt index 5e33bd8f1..be75f6300 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/View.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/View.kt @@ -158,6 +158,8 @@ internal fun View.getWindowVisibleDisplayFrame(): Rect = with(Rect()) { } @VisibleForTesting +@Suppress("DEPRECATION") +// https://github.com/mozilla-mobile/fenix/issues/19929 internal fun View.getKeyboardHeight(): Int { val windowRect = getWindowVisibleDisplayFrame() val statusBarHeight = windowRect.top diff --git a/app/src/main/java/org/mozilla/fenix/gecko/GeckoProvider.kt b/app/src/main/java/org/mozilla/fenix/gecko/GeckoProvider.kt index efec1dc26..f98924fb0 100644 --- a/app/src/main/java/org/mozilla/fenix/gecko/GeckoProvider.kt +++ b/app/src/main/java/org/mozilla/fenix/gecko/GeckoProvider.kt @@ -77,16 +77,19 @@ object GeckoProvider { .getHashUrl(CN_GET_HASH_URL) .build() - runtimeSettings.contentBlocking.setSafeBrowsingProviders(mozcn, + runtimeSettings.contentBlocking.setSafeBrowsingProviders( + mozcn, // Keep the existing configuration ContentBlocking.GOOGLE_SAFE_BROWSING_PROVIDER, - ContentBlocking.GOOGLE_LEGACY_SAFE_BROWSING_PROVIDER) + ContentBlocking.GOOGLE_LEGACY_SAFE_BROWSING_PROVIDER + ) runtimeSettings.contentBlocking.setSafeBrowsingPhishingTable( "m6eb-phish-shavar", "m6ib-phish-shavar", // Existing configuration - "goog-phish-proto") + "goog-phish-proto" + ) } val geckoRuntime = GeckoRuntime.create(context, runtimeSettings) diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataFeature.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataFeature.kt new file mode 100644 index 000000000..6d079ed32 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataFeature.kt @@ -0,0 +1,58 @@ +/* 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.historymetadata + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import mozilla.components.concept.storage.HistoryMetadata +import mozilla.components.concept.storage.HistoryMetadataStorage +import mozilla.components.support.base.feature.LifecycleAwareFeature +import org.mozilla.fenix.home.HomeFragment +import org.mozilla.fenix.home.HomeFragmentAction +import org.mozilla.fenix.home.HomeFragmentStore + +/** + * View-bound feature that retrieves a list of history metadata and dispatches updates to the + * [HomeFragmentStore]. + * + * @param homeStore The [HomeFragmentStore] that holds the state of the [HomeFragment]. + * @param historyMetadataStorage The storage manages [HistoryMetadata]. + * @param scope The [CoroutineScope] used to retrieve a list of history metadata. + * @param ioDispatcher The [CoroutineDispatcher] for performing read/write operations. + */ +class HistoryMetadataFeature( + private val homeStore: HomeFragmentStore, + private val historyMetadataStorage: HistoryMetadataStorage, + private val scope: CoroutineScope, + private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO +) : LifecycleAwareFeature { + + private var job: Job? = null + + override fun start() { + job = scope.launch(ioDispatcher) { + // For now, group the queried list of [HistoryMetadata] according to their search term. + // This feature will later be used to generate more groups. + val historyMetadata = historyMetadataStorage.getHistoryMetadataSince(Long.MIN_VALUE) + .filter { it.totalViewTime > 0 && it.key.searchTerm != null } + .groupBy { it.key.searchTerm!! } + .map { (title, data) -> + HistoryMetadataGroup( + title = title, + historyMetadata = data + ) + } + + homeStore.dispatch(HomeFragmentAction.HistoryMetadataChange(historyMetadata)) + } + } + + override fun stop() { + job?.cancel() + } +} diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataGroup.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataGroup.kt new file mode 100644 index 000000000..801244a31 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataGroup.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.historymetadata + +import mozilla.components.concept.storage.HistoryMetadata + +/** + * A history metadata group. + * + * @property title The title of the group. + * @property historyMetadata A list of [HistoryMetadata] records that matches the title. + * @property expanded Whether or not the group is expanded. + */ +data class HistoryMetadataGroup( + val title: String, + val historyMetadata: List, + val expanded: Boolean = false +) diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddleware.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddleware.kt new file mode 100644 index 000000000..335206c82 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataMiddleware.kt @@ -0,0 +1,156 @@ +/* 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.historymetadata + +import mozilla.components.browser.state.action.BrowserAction +import mozilla.components.browser.state.action.ContentAction +import mozilla.components.browser.state.action.HistoryMetadataAction +import mozilla.components.browser.state.action.MediaSessionAction +import mozilla.components.browser.state.action.TabListAction +import mozilla.components.browser.state.selector.findNormalTab +import mozilla.components.browser.state.selector.findTab +import mozilla.components.browser.state.selector.selectedNormalTab +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.feature.search.ext.parseSearchTerms +import mozilla.components.lib.state.Middleware +import mozilla.components.lib.state.MiddlewareContext +import mozilla.components.lib.state.Store +import mozilla.components.support.base.log.logger.Logger + +/** + * This [Middleware] reacts to various browsing events and records history metadata as needed. + */ +class HistoryMetadataMiddleware( + private val historyMetadataService: HistoryMetadataService +) : Middleware { + + private val logger = Logger("HistoryMetadataMiddleware") + + @Suppress("ComplexMethod") + override fun invoke( + context: MiddlewareContext, + next: (BrowserAction) -> Unit, + action: BrowserAction + ) { + // Pre process actions + when (action) { + is TabListAction.AddTabAction -> { + if (action.select) { + // Before we add and select a new tab we update the metadata + // of the currently selected tab, if not private. + context.state.selectedNormalTab?.let { + updateHistoryMetadata(it) + } + } + } + is TabListAction.SelectTabAction -> { + // Before we select a new tab we update the metadata + // of the currently selected tab, if not private. + context.state.selectedNormalTab?.let { + updateHistoryMetadata(it) + } + } + is TabListAction.RemoveTabAction -> { + if (action.tabId == context.state.selectedTabId) { + context.state.findNormalTab(action.tabId)?.let { + updateHistoryMetadata(it) + } + } + } + is TabListAction.RemoveTabsAction -> { + action.tabIds.find { it == context.state.selectedTabId }?.let { + context.state.findNormalTab(it)?.let { tab -> + updateHistoryMetadata(tab) + } + } + } + is ContentAction.UpdateLoadingStateAction -> { + context.state.findNormalTab(action.sessionId)?.let { tab -> + val selectedTab = tab.id == context.state.selectedTabId + if (!tab.content.loading && action.loading && selectedTab) { + // When a page starts loading (e.g. user navigated away by + // clicking on a link) we update metadata for the selected + // (i.e. previous) url of this tab. + updateHistoryMetadata(tab) + } + } + } + } + + next(action) + + // Post process actions. At this point, tab state will be up-to-date and will possess any + // changes introduced by the action. These handlers rely on up-to-date tab state, which + // is why they're in the "post" section. + when (action) { + // NB: sometimes this fires multiple times after the page finished loading. + is ContentAction.UpdateHistoryStateAction -> { + context.state.findNormalTab(action.sessionId)?.let { tab -> + // When history state is ready, we can record metadata for this page. + val knownHistoryMetadata = tab.historyMetadata + val metadataPresentForUrl = knownHistoryMetadata != null && + knownHistoryMetadata.url == tab.content.url + // Record metadata for tab if there is no metadata present, or if url of the + // tab changes since we last recorded metadata. + if (!metadataPresentForUrl) { + createHistoryMetadata(context, tab) + } + } + } + // NB: this could be called bunch of times in quick succession. + is MediaSessionAction.UpdateMediaMetadataAction -> { + context.state.findNormalTab(action.tabId)?.let { tab -> + createHistoryMetadata(context, tab) + } + } + } + } + + private fun createHistoryMetadata(context: MiddlewareContext, tab: TabSessionState) { + val tabParent = tab.getParent(context.store) + val previousUrlIndex = tab.content.history.currentIndex - 1 + + // Obtain search terms and referrer url either from tab parent, or from the history stack. + val (searchTerm, referrerUrl) = when { + tabParent != null -> { + val searchTerms = tabParent.content.searchTerms.takeUnless { it.isEmpty() } + ?: context.state.search.parseSearchTerms(tabParent.content.url) + searchTerms to tabParent.content.url + } + previousUrlIndex >= 0 -> { + val previousUrl = tab.content.history.items[previousUrlIndex].uri + val searchTerms = context.state.search.parseSearchTerms(previousUrl) + if (searchTerms != null) { + searchTerms to previousUrl + } else { + null to null + } + } + else -> null to null + } + + // Sanity check to make sure we don't record a metadata record referring to itself. + if (tab.content.url == referrerUrl) { + logger.debug("Current url same as referrer. Skipping metadata recording.") + return + } + + val key = historyMetadataService.createMetadata(tab, searchTerm, referrerUrl) + context.dispatch(HistoryMetadataAction.SetHistoryMetadataKeyAction(tab.id, key)) + } + + private fun updateHistoryMetadata(tab: TabSessionState) { + tab.historyMetadata?.let { + historyMetadataService.updateMetadata(it, tab) + } + } + + private fun TabSessionState.getParent(store: Store): TabSessionState? { + return parentId?.let { + store.state.findTab(it) + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataService.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataService.kt new file mode 100644 index 000000000..6f9df959a --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/HistoryMetadataService.kt @@ -0,0 +1,108 @@ +/* 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.historymetadata + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.concept.storage.DocumentType +import mozilla.components.concept.storage.HistoryMetadataKey +import mozilla.components.concept.storage.HistoryMetadataObservation +import mozilla.components.concept.storage.HistoryMetadataStorage +import mozilla.components.support.base.log.logger.Logger + +/** + * Service for managing (creating, updating, deleting) history metadata. + */ +interface HistoryMetadataService { + + /** + * Creates a history metadata record for the provided tab. + * + * @param tab the [TabSessionState] to record metadata for. + * @param searchTerms Search terms associated with this metadata. + * @param referrerUrl Referrer url associated with this metadata. + */ + fun createMetadata( + tab: TabSessionState, + searchTerms: String? = null, + referrerUrl: String? = null + ): HistoryMetadataKey + + /** + * Updates the history metadata corresponding to the provided tab. + * + * @param key the [HistoryMetadataKey] identifying history metadata. + * @param tab the [TabSessionState] to update history metadata for. + */ + fun updateMetadata(key: HistoryMetadataKey, tab: TabSessionState) + + /** + * Deletes history metadata records that haven't been updated since + * the specified timestamp. + * + * @param olderThan timestamp indicating which records to delete. + */ + fun cleanup(olderThan: Long) +} + +class DefaultHistoryMetadataService( + private val storage: HistoryMetadataStorage, + private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO) +) : HistoryMetadataService { + + private val logger = Logger("DefaultHistoryMetadataService") + + override fun createMetadata(tab: TabSessionState, searchTerms: String?, referrerUrl: String?): HistoryMetadataKey { + logger.debug("Creating metadata for tab ${tab.id}") + + val existingMetadata = tab.historyMetadata + val metadataKey = if (existingMetadata != null && existingMetadata.url == tab.content.url) { + existingMetadata + } else { + tab.toHistoryMetadataKey(searchTerms, referrerUrl) + } + + val documentTypeObservation = HistoryMetadataObservation.DocumentTypeObservation( + documentType = when (tab.mediaSessionState) { + null -> DocumentType.Regular + else -> DocumentType.Media + } + ) + + scope.launch { + storage.noteHistoryMetadataObservation(metadataKey, documentTypeObservation) + } + + return metadataKey + } + + override fun updateMetadata(key: HistoryMetadataKey, tab: TabSessionState) { + logger.debug("Updating metadata for tab $tab") + + scope.launch { + val viewTimeObservation = HistoryMetadataObservation.ViewTimeObservation( + viewTime = (System.currentTimeMillis() - tab.lastAccess).toInt() + ) + storage.noteHistoryMetadataObservation(key, viewTimeObservation) + } + } + + override fun cleanup(olderThan: Long) { + logger.debug("Deleting metadata last updated before $olderThan") + + scope.launch { + storage.deleteHistoryMetadataOlderThan(olderThan) + } + } +} + +fun TabSessionState.toHistoryMetadataKey(searchTerms: String?, referrerUrl: String?): HistoryMetadataKey = + HistoryMetadataKey( + url = content.url, + referrerUrl = referrerUrl, + searchTerm = searchTerms + ) diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataController.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataController.kt new file mode 100644 index 000000000..6d783ffd1 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/controller/HistoryMetadataController.kt @@ -0,0 +1,91 @@ +/* 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.historymetadata.controller + +import androidx.annotation.VisibleForTesting +import androidx.annotation.VisibleForTesting.PRIVATE +import androidx.navigation.NavController +import mozilla.components.concept.storage.HistoryMetadataKey +import mozilla.components.feature.tabs.TabsUseCases +import org.mozilla.fenix.BrowserDirection +import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.R +import org.mozilla.fenix.historymetadata.HistoryMetadataGroup +import org.mozilla.fenix.historymetadata.interactor.HistoryMetadataInteractor +import org.mozilla.fenix.home.HomeFragmentAction +import org.mozilla.fenix.home.HomeFragmentDirections +import org.mozilla.fenix.home.HomeFragmentStore +import org.mozilla.fenix.utils.Settings + +/** + * An interface that handles the view manipulation of the history metadata in the Home screen. + */ +interface HistoryMetadataController { + + /** + * @see [HistoryMetadataInteractor.onHistoryMetadataItemClicked] + */ + fun handleHistoryMetadataItemClicked(url: String, historyMetadata: HistoryMetadataKey) + + /** + * @see [HistoryMetadataInteractor.onHistoryMetadataShowAllClicked] + */ + fun handleHistoryShowAllClicked() + + /** + * @see [HistoryMetadataInteractor.onToggleHistoryMetadataGroupExpanded] + */ + fun handleToggleHistoryMetadataGroupExpanded(historyMetadataGroup: HistoryMetadataGroup) +} + +/** + * The default implementation of [HistoryMetadataController]. + */ +class DefaultHistoryMetadataController( + private val activity: HomeActivity, + private val settings: Settings, + private val homeFragmentStore: HomeFragmentStore, + private val selectOrAddUseCase: TabsUseCases.SelectOrAddUseCase, + private val navController: NavController +) : HistoryMetadataController { + + override fun handleHistoryMetadataItemClicked( + url: String, + historyMetadata: HistoryMetadataKey + ) { + val tabId = selectOrAddUseCase.invoke( + url = url, + historyMetadata = historyMetadata + ) + + if (settings.openNextTabInDesktopMode) { + activity.handleRequestDesktopMode(tabId) + } + + activity.openToBrowser(BrowserDirection.FromHome) + } + + override fun handleHistoryShowAllClicked() { + dismissSearchDialogIfDisplayed() + navController.navigate( + HomeFragmentDirections.actionGlobalHistoryFragment() + ) + } + + override fun handleToggleHistoryMetadataGroupExpanded(historyMetadataGroup: HistoryMetadataGroup) { + homeFragmentStore.dispatch( + HomeFragmentAction.HistoryMetadataExpanded( + historyMetadataGroup + ) + ) + } + + @VisibleForTesting(otherwise = PRIVATE) + fun dismissSearchDialogIfDisplayed() { + if (navController.currentDestination?.id == R.id.searchDialogFragment) { + navController.navigateUp() + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/interactor/HistoryMetadataInteractor.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/interactor/HistoryMetadataInteractor.kt new file mode 100644 index 000000000..6f2e085f4 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/interactor/HistoryMetadataInteractor.kt @@ -0,0 +1,37 @@ +/* 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.historymetadata.interactor + +import mozilla.components.concept.storage.HistoryMetadataKey +import org.mozilla.fenix.historymetadata.HistoryMetadataGroup + +/** + * Interface for history metadata related actions in the Home screen. + */ +interface HistoryMetadataInteractor { + + /** + * Selects an existing tab with the matching [HistoryMetadataKey] or adds a new tab with the + * given [url]. Called when a user clicks on a history metadata item. + * + * @param url The URL to open. + * @param historyMetadata The [HistoryMetadataKey] to match for an existing tab. + */ + fun onHistoryMetadataItemClicked(url: String, historyMetadata: HistoryMetadataKey) + + /** + * Shows the history fragment. Called when a user clicks on the "Show all" button besides the + * history metadata header. + */ + fun onHistoryMetadataShowAllClicked() + + /** + * Toggles whether or not a history metadata group is expanded. Called when a user clicks on + * a history metadata group. + * + * @param historyMetadataGroup The [HistoryMetadataGroup] to toggle its expanded state. + */ + fun onToggleHistoryMetadataGroupExpanded(historyMetadataGroup: HistoryMetadataGroup) +} diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataGroupViewHolder.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataGroupViewHolder.kt new file mode 100644 index 000000000..5a103db52 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataGroupViewHolder.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.historymetadata.view + +import android.view.View +import kotlinx.android.synthetic.main.history_metadata_group.* +import org.mozilla.fenix.R +import org.mozilla.fenix.historymetadata.HistoryMetadataGroup +import org.mozilla.fenix.historymetadata.interactor.HistoryMetadataInteractor +import org.mozilla.fenix.utils.view.ViewHolder + +/** + * View holder for a history metadata group item. + * + * @property interactor [HistoryMetadataInteractor] which will have delegated to all user + * interactions. + */ +class HistoryMetadataGroupViewHolder( + view: View, + private val interactor: HistoryMetadataInteractor +) : ViewHolder(view) { + + fun bind(historyMetadataGroup: HistoryMetadataGroup) { + history_metadata_group_title.text = historyMetadataGroup.title + + itemView.isActivated = historyMetadataGroup.expanded + + itemView.setOnClickListener { + interactor.onToggleHistoryMetadataGroupExpanded(historyMetadataGroup) + } + } + + companion object { + const val LAYOUT_ID = R.layout.history_metadata_group + } +} diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataHeaderViewHolder.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataHeaderViewHolder.kt new file mode 100644 index 000000000..6118dbf7d --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataHeaderViewHolder.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.historymetadata.view + +import android.view.View +import kotlinx.android.synthetic.main.history_metadata_header.* +import org.mozilla.fenix.R +import org.mozilla.fenix.historymetadata.interactor.HistoryMetadataInteractor +import org.mozilla.fenix.utils.view.ViewHolder + +/** + * View holder for the history metadata header and "Show all" button. + * + * @property interactor [HistoryMetadataInteractor] which will have delegated to all user + * interactions. + */ +class HistoryMetadataHeaderViewHolder( + view: View, + private val interactor: HistoryMetadataInteractor +) : ViewHolder(view) { + + init { + show_all_button.setOnClickListener { + interactor.onHistoryMetadataShowAllClicked() + } + } + + companion object { + const val LAYOUT_ID = R.layout.history_metadata_header + } +} diff --git a/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataViewHolder.kt b/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataViewHolder.kt new file mode 100644 index 000000000..43f0dff7f --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/historymetadata/view/HistoryMetadataViewHolder.kt @@ -0,0 +1,49 @@ +/* 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.historymetadata.view + +import android.view.View +import kotlinx.android.synthetic.main.history_metadata_list_row.* +import mozilla.components.browser.icons.BrowserIcons +import mozilla.components.browser.state.state.ContentState +import mozilla.components.concept.storage.HistoryMetadata +import org.mozilla.fenix.R +import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.loadIntoView +import org.mozilla.fenix.historymetadata.interactor.HistoryMetadataInteractor +import org.mozilla.fenix.utils.view.ViewHolder + +/** + * View holder for a history metadata item. + * + * @property interactor [HistoryMetadataInteractor] which will have delegated to all user + * interactions. + * @property icons an instance of [BrowserIcons] for rendering the sites icon if one isn't found + * in [ContentState.icon]. + */ +class HistoryMetadataViewHolder( + view: View, + private val interactor: HistoryMetadataInteractor, + private val icons: BrowserIcons = view.context.components.core.icons +) : ViewHolder(view) { + + fun bind(historyMetadata: HistoryMetadata) { + history_metadata_title.text = if (historyMetadata.title.isNullOrEmpty()) { + historyMetadata.key.url + } else { + historyMetadata.title + } + + icons.loadIntoView(history_metadata_icon, historyMetadata.key.url) + + itemView.setOnClickListener { + interactor.onHistoryMetadataItemClicked(historyMetadata.key.url, historyMetadata.key) + } + } + + companion object { + const val LAYOUT_ID = R.layout.history_metadata_list_row + } +} 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 a43261f41..962cb17f0 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -45,16 +45,8 @@ import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE import com.google.android.material.appbar.AppBarLayout import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.fragment_home.* -import kotlinx.android.synthetic.main.fragment_home.view.bottomBarShadow -import kotlinx.android.synthetic.main.fragment_home.view.bottom_bar -import kotlinx.android.synthetic.main.fragment_home.view.homeAppBar -import kotlinx.android.synthetic.main.fragment_home.view.menuButton -import kotlinx.android.synthetic.main.fragment_home.view.sessionControlRecyclerView -import kotlinx.android.synthetic.main.fragment_home.view.tab_button -import kotlinx.android.synthetic.main.fragment_home.view.toolbar -import kotlinx.android.synthetic.main.fragment_home.view.toolbarLayout -import kotlinx.android.synthetic.main.fragment_home.view.toolbar_wrapper -import kotlinx.android.synthetic.main.no_collections_message.view.add_tabs_to_collections_button +import kotlinx.android.synthetic.main.fragment_home.view.* +import kotlinx.android.synthetic.main.no_collections_message.view.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -85,6 +77,7 @@ import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged import mozilla.components.ui.tabcounter.TabCounterMenu import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.Config +import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.GleanMetrics.PerfStartup import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R @@ -95,6 +88,7 @@ import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.PrivateShortcutCreateManager import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.TabCollectionStorage +import org.mozilla.fenix.components.accounts.AccountState import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.tips.FenixTipManager import org.mozilla.fenix.components.tips.Tip @@ -103,14 +97,19 @@ import org.mozilla.fenix.components.toolbar.FenixTabCounterMenu import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.hideToolbar -import org.mozilla.fenix.ext.navigateBlockingForAsyncNavGraph import org.mozilla.fenix.ext.measureNoInline import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.runIfFragmentIsAttached import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.historymetadata.HistoryMetadataFeature +import org.mozilla.fenix.historymetadata.controller.DefaultHistoryMetadataController import org.mozilla.fenix.home.mozonline.showPrivacyPopWindow +import org.mozilla.fenix.home.recentbookmarks.RecentBookmarksFeature +import org.mozilla.fenix.home.recentbookmarks.controller.DefaultRecentBookmarksController +import org.mozilla.fenix.home.recenttabs.RecentTabsListFeature +import org.mozilla.fenix.home.recenttabs.controller.DefaultRecentTabsController import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor import org.mozilla.fenix.home.sessioncontrol.SessionControlView @@ -171,6 +170,9 @@ class HomeFragment : Fragment() { private lateinit var currentMode: CurrentMode private val topSitesFeature = ViewBoundFeatureWrapper() + private val recentTabsListFeature = ViewBoundFeatureWrapper() + private val recentBookmarksFeature = ViewBoundFeatureWrapper() + private val historyMetadataFeature = ViewBoundFeatureWrapper() @VisibleForTesting internal var getMenuButton: () -> MenuButton? = { menuButton } @@ -187,7 +189,8 @@ class HomeFragment : Fragment() { if (!onboarding.userHasBeenOnboarded() && requireContext().settings().shouldShowPrivacyPopWindow && - Config.channel.isMozillaOnline) { + Config.channel.isMozillaOnline + ) { showPrivacyPopWindow(requireContext(), requireActivity()) } } @@ -227,8 +230,11 @@ class HomeFragment : Fragment() { ) ).getTip() }, + recentBookmarks = emptyList(), showCollectionPlaceholder = components.settings.showCollectionsPlaceholderOnHome, - showSetAsDefaultBrowserCard = components.settings.shouldShowSetAsDefaultBrowserCard() + showSetAsDefaultBrowserCard = components.settings.shouldShowSetAsDefaultBrowserCard(), + recentTabs = emptyList(), + historyMetadata = emptyList() ) ) } @@ -243,8 +249,45 @@ class HomeFragment : Fragment() { view = view ) + if (FeatureFlags.showRecentTabsFeature) { + recentTabsListFeature.set( + feature = RecentTabsListFeature( + browserStore = components.core.store, + homeStore = homeFragmentStore + ), + owner = viewLifecycleOwner, + view = view + ) + } + + if (FeatureFlags.recentBookmarksFeature) { + recentBookmarksFeature.set( + feature = RecentBookmarksFeature( + homeStore = homeFragmentStore, + bookmarksUseCase = run { + requireContext().components.useCases.bookmarksUseCases + }, + scope = viewLifecycleOwner.lifecycleScope + ), + owner = viewLifecycleOwner, + view = view + ) + } + + if (requireContext().settings().historyMetadataFeature) { + historyMetadataFeature.set( + feature = HistoryMetadataFeature( + homeStore = homeFragmentStore, + historyMetadataStorage = components.core.historyStorage, + scope = viewLifecycleOwner.lifecycleScope + ), + owner = viewLifecycleOwner, + view = view + ) + } + _sessionControlInteractor = SessionControlInteractor( - DefaultSessionControlController( + controller = DefaultSessionControlController( activity = activity, settings = components.settings, engine = components.core.engine, @@ -255,15 +298,31 @@ class HomeFragment : Fragment() { restoreUseCase = components.useCases.tabsUseCases.restore, reloadUrlUseCase = components.useCases.sessionUseCases.reload, selectTabUseCase = components.useCases.tabsUseCases.selectTab, - requestDesktopSiteUseCase = components.useCases.sessionUseCases.requestDesktopSite, fragmentStore = homeFragmentStore, navController = findNavController(), viewLifecycleScope = viewLifecycleOwner.lifecycleScope, hideOnboarding = ::hideOnboardingAndOpenSearch, registerCollectionStorageObserver = ::registerCollectionStorageObserver, showDeleteCollectionPrompt = ::showDeleteCollectionPrompt, - showTabTray = ::openTabTray, + showTabTray = ::openTabsTray, handleSwipedItemDeletionCancel = ::handleSwipedItemDeletionCancel + ), + recentTabController = DefaultRecentTabsController( + selectTabUseCase = components.useCases.tabsUseCases.selectTab, + navController = findNavController(), + metrics = requireComponents.analytics.metrics, + store = components.core.store + ), + recentBookmarksController = DefaultRecentBookmarksController( + activity = activity, + navController = findNavController() + ), + historyMetadataController = DefaultHistoryMetadataController( + activity = activity, + settings = components.settings, + homeFragmentStore = homeFragmentStore, + selectOrAddUseCase = components.useCases.tabsUseCases.selectOrAddTab, + navController = findNavController() ) ) @@ -396,7 +455,10 @@ class HomeFragment : Fragment() { } view.tab_button.setOnClickListener { - openTabTray() + if (FeatureFlags.showStartOnHomeSettings) { + requireComponents.analytics.metrics.track(Event.StartOnHomeOpenTabsTray) + } + openTabsTray() } PrivateBrowsingButtonView( @@ -536,7 +598,7 @@ class HomeFragment : Fragment() { requireContext().getString(R.string.snackbar_deleted_undo), { requireComponents.useCases.tabsUseCases.undo.invoke() - findNavController().navigateBlockingForAsyncNavGraph( + findNavController().navigate( HomeFragmentDirections.actionGlobalBrowser(null) ) }, @@ -578,7 +640,10 @@ class HomeFragment : Fragment() { ) ).getTip() }, - showCollectionPlaceholder = components.settings.showCollectionsPlaceholderOnHome + showCollectionPlaceholder = components.settings.showCollectionsPlaceholderOnHome, + recentTabs = emptyList(), + recentBookmarks = emptyList(), + historyMetadata = emptyList() ) ) @@ -592,22 +657,25 @@ class HomeFragment : Fragment() { currentMode, owner = this@HomeFragment.viewLifecycleOwner ) - requireComponents.backgroundServices.accountManager.register(object : AccountObserver { - override fun onAuthenticated(account: OAuthAccount, authType: AuthType) { - if (authType != AuthType.Existing) { - view?.let { - FenixSnackbar.make( - view = it, - duration = Snackbar.LENGTH_SHORT, - isDisplayedWithBrowserToolbar = false - ) - .setText(it.context.getString(R.string.onboarding_firefox_account_sync_is_on)) - .setAnchorView(toolbarLayout) - .show() + requireComponents.backgroundServices.accountManager.register( + object : AccountObserver { + override fun onAuthenticated(account: OAuthAccount, authType: AuthType) { + if (authType != AuthType.Existing) { + view?.let { + FenixSnackbar.make( + view = it, + duration = Snackbar.LENGTH_SHORT, + isDisplayedWithBrowserToolbar = false + ) + .setText(it.context.getString(R.string.onboarding_firefox_account_sync_is_on)) + .setAnchorView(toolbarLayout) + .show() + } } } - } - }, owner = this@HomeFragment.viewLifecycleOwner) + }, + owner = this@HomeFragment.viewLifecycleOwner + ) } if (browsingModeManager.mode.isPrivate && @@ -627,8 +695,9 @@ class HomeFragment : Fragment() { } private fun navToSavedLogins() { - findNavController().navigateBlockingForAsyncNavGraph( - HomeFragmentDirections.actionGlobalSavedLoginsAuthFragment()) + findNavController().navigate( + HomeFragmentDirections.actionGlobalSavedLoginsAuthFragment() + ) } private fun dispatchModeChanges(mode: Mode) { @@ -706,14 +775,12 @@ class HomeFragment : Fragment() { ) layout.findViewById