diff --git a/app/build.gradle b/app/build.gradle index 0461d9a2f1..44fa7a80b7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -369,7 +369,6 @@ dependencies { implementation Deps.androidx_coordinatorlayout implementation Deps.sentry - implementation Deps.osslicenses_library implementation Deps.leanplum_core implementation Deps.leanplum_fcm diff --git a/app/metrics.yaml b/app/metrics.yaml index 5a15283cbd..230f67c14a 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -34,6 +34,8 @@ events: - https://github.com/mozilla-mobile/fenix/issues/11830 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12114#pullrequestreview-445245341 + data_sensitivity: + - interaction notification_emails: - esmyth@mozilla.com - perf-android-fe@mozilla.com @@ -78,6 +80,8 @@ events: - https://github.com/mozilla-mobile/fenix/issues/10616 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673 + data_sensitivity: + - interaction notification_emails: - telemetry-client-dev@mozilla.com - fenix-core@mozilla.com @@ -95,6 +99,8 @@ events: - https://github.com/mozilla-mobile/fenix/issues/959 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -111,6 +117,8 @@ events: - https://github.com/mozilla-mobile/fenix/issues/959 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -133,6 +141,8 @@ events: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673 - https://github.com/mozilla-mobile/fenix/pull/1677 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -156,6 +166,8 @@ events: - https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708 - https://github.com/mozilla-mobile/fenix/pull/5098#issuecomment-529658996 - https://github.com/mozilla-mobile/fenix/pull/6310 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -175,6 +187,8 @@ events: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1785 - https://github.com/mozilla-mobile/fenix/pull/8314 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -213,6 +227,9 @@ events: - https://github.com/mozilla-mobile/fenix/pull/6352 - https://github.com/mozilla-mobile/fenix/pull/6601 - https://github.com/mozilla-mobile/fenix/pull/6746 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -224,6 +241,8 @@ events: - https://github.com/mozilla-mobile/fenix/issues/5021 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5090 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -239,6 +258,8 @@ events: - https://github.com/mozilla-mobile/fenix/issues/5737 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5975 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -257,6 +278,8 @@ events: - https://github.com/mozilla-mobile/fenix/issues/11442 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11533 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -270,6 +293,8 @@ onboarding: - https://github.com/mozilla-mobile/fenix/issues/10824 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11867 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -282,6 +307,8 @@ onboarding: - https://github.com/mozilla-mobile/fenix/issues/10824 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11867 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -294,6 +321,8 @@ onboarding: - https://github.com/mozilla-mobile/fenix/issues/10824 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11867 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -306,6 +335,8 @@ onboarding: - https://github.com/mozilla-mobile/fenix/issues/10824 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11867 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -323,6 +354,8 @@ onboarding: - https://github.com/mozilla-mobile/fenix/issues/10824 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11867 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -340,6 +373,8 @@ onboarding: - https://github.com/mozilla-mobile/fenix/issues/10824 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11867 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -352,6 +387,8 @@ onboarding: - https://github.com/mozilla-mobile/fenix/issues/10824 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11867 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -369,6 +406,8 @@ onboarding: - https://github.com/mozilla-mobile/fenix/issues/10824 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11867 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -381,6 +420,8 @@ onboarding: - https://github.com/mozilla-mobile/fenix/issues/10824 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11867 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -416,6 +457,8 @@ toolbar_settings: - https://github.com/mozilla-mobile/fenix/issues/6054 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6608 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -429,6 +472,8 @@ crash_reporter: - https://github.com/mozilla-mobile/fenix/issues/1040 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -445,6 +490,8 @@ crash_reporter: - https://github.com/mozilla-mobile/fenix/issues/1040 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1214#issue-264756708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -467,6 +514,8 @@ context_menu: - https://github.com/mozilla-mobile/fenix/issues/957 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -480,6 +529,8 @@ login_dialog: - https://github.com/mozilla-mobile/fenix/issues/9730 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13050 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2021-02-01" @@ -491,6 +542,8 @@ login_dialog: - https://github.com/mozilla-mobile/fenix/issues/9730 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13050 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2021-02-01" @@ -502,6 +555,8 @@ login_dialog: - https://github.com/mozilla-mobile/fenix/issues/9730 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13050 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2021-02-01" @@ -513,6 +568,8 @@ login_dialog: - https://github.com/mozilla-mobile/fenix/issues/9730 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13050 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2021-02-01" @@ -526,6 +583,8 @@ find_in_page: - https://github.com/mozilla-mobile/fenix/issues/1036 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -537,6 +596,8 @@ find_in_page: - https://github.com/mozilla-mobile/fenix/issues/1036 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -548,6 +609,8 @@ find_in_page: - https://github.com/mozilla-mobile/fenix/issues/1036 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1344#issuecomment-479285010 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -564,6 +627,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/960 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1067#issuecomment-474598673 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -582,6 +647,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/8125 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9556 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -596,6 +663,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/8125 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9556 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -618,6 +687,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/11909 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11982#pullrequestreview-437963817 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-12-01" @@ -633,6 +704,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/11909 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11982#pullrequestreview-437963817 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-12-01" @@ -658,6 +731,9 @@ metrics: - https://github.com/mozilla-mobile/fenix/pull/1677 - https://github.com/mozilla-mobile/fenix/pull/5216 - https://github.com/mozilla-mobile/fenix/pull/7310 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -676,6 +752,9 @@ metrics: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1953/ - https://github.com/mozilla-mobile/fenix/pull/5216 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -692,6 +771,9 @@ metrics: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1953/ - https://github.com/mozilla-mobile/fenix/pull/5216 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -709,6 +791,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/9136 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5579 + data_sensitivity: + - technical notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -726,6 +810,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/9136 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9253 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -743,6 +829,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/9136 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9253 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -760,6 +848,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/9136 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9253 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -774,6 +864,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/6054 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6608 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -788,6 +880,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/9488 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10958 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -806,6 +900,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/11479 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12024 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -820,6 +916,8 @@ metrics: - https://github.com/mozilla-mobile/fenix/issues/11479 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12024 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -836,6 +934,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -850,6 +950,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -866,6 +968,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -881,6 +985,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -895,6 +1001,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -909,6 +1017,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -923,6 +1033,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -937,6 +1049,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -951,6 +1065,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -965,6 +1081,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -981,6 +1099,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -995,6 +1115,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1010,6 +1132,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1024,6 +1148,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1039,6 +1165,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11211 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1053,6 +1181,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11446 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1067,6 +1197,8 @@ preferences: - https://github.com/mozilla-mobile/fenix/issues/11118 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11446 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1087,6 +1219,9 @@ search.default_engine: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1606 - https://github.com/mozilla-mobile/fenix/pull/5216 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1105,6 +1240,9 @@ search.default_engine: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1606 - https://github.com/mozilla-mobile/fenix/pull/5216 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1124,6 +1262,9 @@ search.default_engine: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1606 - https://github.com/mozilla-mobile/fenix/pull/5216 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1137,6 +1278,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/974 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1148,6 +1291,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/974 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1159,6 +1304,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/974 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1170,6 +1317,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/974 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1181,6 +1330,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/974 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1192,6 +1343,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/974 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1203,6 +1356,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/974 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1214,6 +1369,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/974 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1225,6 +1382,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/974 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1236,6 +1395,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/974 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1247,6 +1408,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/974 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1708 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1258,6 +1421,8 @@ bookmarks_management: - https://github.com/mozilla-mobile/fenix/issues/3174 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3724 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1271,6 +1436,8 @@ custom_tab: - https://github.com/mozilla-mobile/fenix/issues/977 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1697 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1282,6 +1449,8 @@ custom_tab: - https://github.com/mozilla-mobile/fenix/issues/977 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1697 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1293,6 +1462,8 @@ custom_tab: - https://github.com/mozilla-mobile/fenix/issues/977 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1697 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1311,6 +1482,8 @@ activation: - https://bugzilla.mozilla.org/1501822 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1707#issuecomment-486972209 + data_sensitivity: + - highly_sensitive notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1326,6 +1499,8 @@ activation: - https://bugzilla.mozilla.org/1538011 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/1707#issuecomment-486972209 + data_sensitivity: + - highly_sensitive notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1341,6 +1516,8 @@ qr_scanner: - https://github.com/mozilla-mobile/fenix/issues/1857 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1353,6 +1530,8 @@ qr_scanner: - https://github.com/mozilla-mobile/fenix/issues/1857 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1365,6 +1544,8 @@ qr_scanner: - https://github.com/mozilla-mobile/fenix/issues/1857 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1377,6 +1558,8 @@ qr_scanner: - https://github.com/mozilla-mobile/fenix/issues/1857 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2524#issuecomment-492739967 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1393,6 +1576,8 @@ error_page: - https://github.com/mozilla-mobile/fenix/issues/1242 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2491#issuecomment-492414486 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1406,6 +1591,8 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/1190 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1417,6 +1604,8 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/1190 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1429,6 +1618,8 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/9834 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9835#pullrequestreview-398641844 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1440,6 +1631,8 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/9834 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9835#pullrequestreview-398641844 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1452,6 +1645,8 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/1190 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1464,6 +1659,8 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/1190 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1475,6 +1672,9 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/4971 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1487,6 +1687,9 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/4971 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1499,6 +1702,9 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/4971 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1511,6 +1717,9 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/4971 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1523,6 +1732,9 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/4971 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4931#issuecomment-529740300 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1534,6 +1746,8 @@ sync_auth: - https://github.com/mozilla-mobile/fenix/issues/1190 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1547,6 +1761,8 @@ sync_account: - https://github.com/mozilla-mobile/fenix/issues/1190 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1558,6 +1774,8 @@ sync_account: - https://github.com/mozilla-mobile/fenix/issues/1190 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1569,6 +1787,8 @@ sync_account: - https://github.com/mozilla-mobile/fenix/issues/1190 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/2745#issuecomment-494918532 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1580,6 +1800,8 @@ sync_account: - https://github.com/mozilla-mobile/fenix/issues/4908 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5106 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1591,6 +1813,8 @@ sync_account: - https://github.com/mozilla-mobile/fenix/issues/4908 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5106 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1604,6 +1828,8 @@ history: - https://github.com/mozilla-mobile/fenix/issues/2362 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3940 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1615,6 +1841,8 @@ history: - https://github.com/mozilla-mobile/fenix/issues/2362 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3940 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1626,6 +1854,8 @@ history: - https://github.com/mozilla-mobile/fenix/issues/2362 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3940 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1637,6 +1867,8 @@ history: - https://github.com/mozilla-mobile/fenix/issues/2362 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3940 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1648,6 +1880,8 @@ history: - https://github.com/mozilla-mobile/fenix/issues/2362 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3940 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1664,6 +1898,8 @@ tip: - https://github.com/mozilla-mobile/fenix/issues/9328 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9836 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1678,6 +1914,8 @@ tip: - https://github.com/mozilla-mobile/fenix/issues/9328 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9836 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1692,6 +1930,8 @@ tip: - https://github.com/mozilla-mobile/fenix/issues/9328 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9836 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1705,6 +1945,8 @@ reader_mode: - https://github.com/mozilla-mobile/fenix/issues/2267 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3941 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1716,6 +1958,8 @@ reader_mode: - https://github.com/mozilla-mobile/fenix/issues/2267 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3941 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1727,6 +1971,8 @@ reader_mode: - https://github.com/mozilla-mobile/fenix/issues/2267 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4328 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1738,6 +1984,8 @@ reader_mode: - https://github.com/mozilla-mobile/fenix/issues/2267 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3941 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1751,6 +1999,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1762,6 +2012,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1773,6 +2025,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1784,6 +2038,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1795,6 +2051,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1806,6 +2064,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1817,6 +2077,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1828,6 +2090,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1839,6 +2103,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1851,6 +2117,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1863,6 +2131,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1875,6 +2145,8 @@ tabs_tray: - https://github.com/mozilla-mobile/fenix/issues/11273 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12036 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1888,6 +2160,9 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3935 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1899,6 +2174,9 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3935 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1910,6 +2188,9 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3935 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1921,6 +2202,9 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3935 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1932,6 +2216,9 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3935 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1943,6 +2230,9 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3935 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1959,6 +2249,9 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3935 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1975,6 +2268,9 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3935 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1987,6 +2283,9 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/3935 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -1998,6 +2297,8 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4358 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2009,6 +2310,8 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4358 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2022,6 +2325,8 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4358 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2038,6 +2343,8 @@ collections: - https://github.com/mozilla-mobile/fenix/issues/969 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4539 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2052,6 +2359,8 @@ search_widget: - https://github.com/mozilla-mobile/fenix/issues/4457 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4714 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2063,6 +2372,8 @@ search_widget: - https://github.com/mozilla-mobile/fenix/issues/4457 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4714 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2076,6 +2387,8 @@ search_widget_cfr: - https://github.com/mozilla-mobile/fenix/issues/9488 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10958 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2087,6 +2400,8 @@ search_widget_cfr: - https://github.com/mozilla-mobile/fenix/issues/9488 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10958 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2098,6 +2413,8 @@ search_widget_cfr: - https://github.com/mozilla-mobile/fenix/issues/9488 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10958 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2110,6 +2427,8 @@ search_widget_cfr: - https://github.com/mozilla-mobile/fenix/issues/9488 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10958 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2124,6 +2443,8 @@ private_browsing_mode: - https://github.com/mozilla-mobile/fenix/issues/4658 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4968 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2136,6 +2457,8 @@ private_browsing_mode: - https://github.com/mozilla-mobile/fenix/issues/4658 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4968 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2147,6 +2470,8 @@ private_browsing_mode: - https://github.com/mozilla-mobile/fenix/issues/4658 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4968 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2158,6 +2483,8 @@ private_browsing_mode: - https://github.com/mozilla-mobile/fenix/issues/4658 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4968 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2170,6 +2497,8 @@ private_browsing_mode: - https://github.com/mozilla-mobile/fenix/issues/4658 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/4968 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2184,6 +2513,8 @@ contextual_hint.tracking_protection: - https://github.com/mozilla-mobile/fenix/issues/9625 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11923 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2197,6 +2528,8 @@ contextual_hint.tracking_protection: - https://github.com/mozilla-mobile/fenix/issues/9625 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11923 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2209,6 +2542,8 @@ contextual_hint.tracking_protection: - https://github.com/mozilla-mobile/fenix/issues/9625 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11923 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2221,6 +2556,8 @@ contextual_hint.tracking_protection: - https://github.com/mozilla-mobile/fenix/issues/9625 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11923 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2235,6 +2572,8 @@ tracking_protection: - https://github.com/mozilla-mobile/fenix/issues/5312 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2246,6 +2585,8 @@ tracking_protection: - https://github.com/mozilla-mobile/fenix/issues/5312 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2257,6 +2598,8 @@ tracking_protection: - https://github.com/mozilla-mobile/fenix/issues/5312 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2269,6 +2612,8 @@ tracking_protection: - https://github.com/mozilla-mobile/fenix/issues/5312 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2280,6 +2625,8 @@ tracking_protection: - https://github.com/mozilla-mobile/fenix/issues/5312 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2297,6 +2644,8 @@ tracking_protection: data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5414#issuecomment-532847188 - https://github.com/mozilla-mobile/fenix/pull/11383 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2310,6 +2659,8 @@ private_browsing_shortcut: - https://github.com/mozilla-mobile/fenix/issues/4658 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5194 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2322,6 +2673,8 @@ private_browsing_shortcut: - https://github.com/mozilla-mobile/fenix/issues/4658 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5194 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2334,6 +2687,8 @@ private_browsing_shortcut: - https://github.com/mozilla-mobile/fenix/issues/4658 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5194 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2346,6 +2701,8 @@ private_browsing_shortcut: - https://github.com/mozilla-mobile/fenix/issues/4658 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5194 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2358,6 +2715,8 @@ private_browsing_shortcut: - https://github.com/mozilla-mobile/fenix/issues/4658 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5194 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2370,6 +2729,8 @@ private_browsing_shortcut: - https://github.com/mozilla-mobile/fenix/issues/4658 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5194 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2383,6 +2744,8 @@ tab: - https://github.com/mozilla-mobile/fenix/issues/5197 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5266 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2396,6 +2759,8 @@ tab: - https://github.com/mozilla-mobile/fenix/issues/5197 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5266 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2409,6 +2774,8 @@ media_notification: - https://github.com/mozilla-mobile/fenix/issues/5197 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5520 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2420,6 +2787,8 @@ media_notification: - https://github.com/mozilla-mobile/fenix/issues/5197 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/5520 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2433,6 +2802,8 @@ media_state: - https://github.com/mozilla-mobile/fenix/issues/5705 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6463 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2444,6 +2815,8 @@ media_state: - https://github.com/mozilla-mobile/fenix/issues/5705 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6463 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2455,6 +2828,8 @@ media_state: - https://github.com/mozilla-mobile/fenix/issues/5705 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6463 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2468,6 +2843,8 @@ logins: - https://github.com/mozilla-mobile/fenix/issues/5586 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6352 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2479,6 +2856,8 @@ logins: - https://github.com/mozilla-mobile/fenix/issues/5586 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6352 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2490,6 +2869,8 @@ logins: - https://github.com/mozilla-mobile/fenix/issues/5586 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6352 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2501,6 +2882,8 @@ logins: - https://github.com/mozilla-mobile/fenix/issues/5586 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6352 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2517,6 +2900,8 @@ logins: - https://github.com/mozilla-mobile/fenix/issues/5586 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/7767 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2528,6 +2913,8 @@ logins: - https://github.com/mozilla-mobile/fenix/issues/10173 data_reviews: - https://github.com/mozilla-mobile/fenix/issues/11208 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2539,6 +2926,8 @@ logins: - https://github.com/mozilla-mobile/fenix/issues/10173 data_reviews: - https://github.com/mozilla-mobile/fenix/issues/11208 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2550,6 +2939,8 @@ logins: - https://github.com/mozilla-mobile/fenix/issues/10173 data_reviews: - https://github.com/mozilla-mobile/fenix/issues/11208 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2563,6 +2954,8 @@ download_notification: - https://github.com/mozilla-mobile/fenix/issues/5583 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6554 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2574,6 +2967,8 @@ download_notification: - https://github.com/mozilla-mobile/fenix/issues/5583 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6554 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2585,6 +2980,8 @@ download_notification: - https://github.com/mozilla-mobile/fenix/issues/5583 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6554 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2597,6 +2994,8 @@ download_notification: - https://github.com/mozilla-mobile/fenix/issues/5583 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6554 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2608,6 +3007,8 @@ download_notification: - https://github.com/mozilla-mobile/fenix/issues/5583 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6554 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2619,6 +3020,8 @@ download_notification: - https://github.com/mozilla-mobile/fenix/issues/5583 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6554 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2631,6 +3034,8 @@ download_notification: - https://github.com/mozilla-mobile/fenix/issues/5583 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6554 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2644,6 +3049,8 @@ user_specified_search_engines: - https://github.com/mozilla-mobile/fenix/issues/5884 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6918 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2658,6 +3065,8 @@ user_specified_search_engines: - https://github.com/mozilla-mobile/fenix/issues/7881 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6918 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2671,6 +3080,9 @@ search_suggestions: - https://github.com/mozilla-mobile/fenix/issues/6070 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/6746 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2684,6 +3096,8 @@ voice_search: - https://github.com/mozilla-mobile/fenix/issues/10465 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10785 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2697,6 +3111,8 @@ top_sites: - https://github.com/mozilla-mobile/fenix/issues/8125 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10752 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2708,6 +3124,8 @@ top_sites: - https://github.com/mozilla-mobile/fenix/issues/6757 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/7523 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2719,6 +3137,8 @@ top_sites: - https://github.com/mozilla-mobile/fenix/issues/6757 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/7523 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2730,6 +3150,8 @@ top_sites: - https://github.com/mozilla-mobile/fenix/issues/6757 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/7523 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2743,6 +3165,8 @@ about_page: - https://github.com/mozilla-mobile/fenix/issues/6834 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8047 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2754,6 +3178,8 @@ about_page: - https://github.com/mozilla-mobile/fenix/issues/6834 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8047 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2765,6 +3191,8 @@ about_page: - https://github.com/mozilla-mobile/fenix/issues/6834 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8047 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2776,6 +3204,8 @@ about_page: - https://github.com/mozilla-mobile/fenix/issues/6834 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8047 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2787,6 +3217,8 @@ about_page: - https://github.com/mozilla-mobile/fenix/issues/6834 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8047 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2805,6 +3237,8 @@ app_theme: - https://github.com/mozilla-mobile/fenix/issues/7289 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/7968 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2818,6 +3252,8 @@ pocket: - https://github.com/mozilla-mobile/fenix/issues/8126 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8098 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2832,6 +3268,8 @@ pocket: - https://github.com/mozilla-mobile/fenix/issues/8126 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8098 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2847,6 +3285,9 @@ first_session: - https://github.com/mozilla-mobile/fenix/issues/7295 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2860,6 +3301,9 @@ first_session: - https://github.com/mozilla-mobile/fenix/issues/7295 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2873,6 +3317,9 @@ first_session: - https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586480836 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2886,6 +3333,9 @@ first_session: - https://github.com/mozilla-mobile/fenix/issues/7295 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2901,6 +3351,9 @@ first_session: - https://github.com/mozilla-mobile/fenix/issues/7295 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8074#issuecomment-586512202 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2917,6 +3370,8 @@ browser.search: - https://github.com/mozilla-mobile/fenix/issues/6558 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10112 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2931,6 +3386,8 @@ browser.search: - https://github.com/mozilla-mobile/fenix/issues/6558 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10112 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2944,6 +3401,8 @@ browser.search: - https://github.com/mozilla-mobile/fenix/issues/6557 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10167 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2957,6 +3416,8 @@ addons: - https://github.com/mozilla-mobile/fenix/issues/6174 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8318 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2972,6 +3433,8 @@ addons: - https://github.com/mozilla-mobile/fenix/issues/6174 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8318 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2985,6 +3448,8 @@ addons: - https://github.com/mozilla-mobile/fenix/issues/6174 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8318 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -2998,6 +3463,8 @@ addons: - https://github.com/mozilla-mobile/fenix/issues/6174 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/8318 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -3011,6 +3478,8 @@ addons: - https://github.com/mozilla-mobile/fenix/issues/8920 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11080 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -3024,6 +3493,8 @@ addons: - https://github.com/mozilla-mobile/fenix/issues/8920 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11080 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2020-10-01" @@ -3048,6 +3519,8 @@ startup.timeline: - https://github.com/mozilla-mobile/fenix/issues/8803 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9788#pullrequestreview-394228626 + data_sensitivity: + - technical notification_emails: - perf-android-fe@mozilla.com - mcomella@mozilla.com @@ -3064,6 +3537,8 @@ startup.timeline: - https://github.com/mozilla-mobile/fenix/issues/8803 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9788#pullrequestreview-394228626 + data_sensitivity: + - technical notification_emails: - perf-android-fe@mozilla.com - mcomella@mozilla.com @@ -3080,6 +3555,8 @@ startup.timeline: - https://github.com/mozilla-mobile/fenix/issues/10434 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10481 + data_sensitivity: + - technical notification_emails: - perf-android-fe@mozilla.com - mcomella@mozilla.com @@ -3096,6 +3573,8 @@ startup.timeline: - https://github.com/mozilla-mobile/fenix/issues/8803 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/9788#pullrequestreview-394228626 + data_sensitivity: + - technical notification_emails: - perf-android-fe@mozilla.com - mcomella@mozilla.com @@ -3113,6 +3592,9 @@ perf.awesomebar: - https://github.com/mozilla-mobile/android-components/issues/4992 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10276#pullrequestreview-411101979 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com - gkruglov@mozilla.com @@ -3128,6 +3610,9 @@ perf.awesomebar: - https://github.com/mozilla-mobile/android-components/issues/4992 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10276#pullrequestreview-411101979 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com - gkruglov@mozilla.com @@ -3143,6 +3628,9 @@ perf.awesomebar: - https://github.com/mozilla-mobile/android-components/issues/4992 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10276#pullrequestreview-411101979 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com - gkruglov@mozilla.com @@ -3158,6 +3646,9 @@ perf.awesomebar: - https://github.com/mozilla-mobile/android-components/issues/4992 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10276#pullrequestreview-411101979 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com - gkruglov@mozilla.com @@ -3173,6 +3664,9 @@ perf.awesomebar: - https://github.com/mozilla-mobile/android-components/issues/4992 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10276#pullrequestreview-411101979 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com - gkruglov@mozilla.com @@ -3188,6 +3682,9 @@ perf.awesomebar: - https://github.com/mozilla-mobile/android-components/issues/4992 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10276#pullrequestreview-411101979 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com - gkruglov@mozilla.com @@ -3203,6 +3700,9 @@ perf.awesomebar: - https://github.com/mozilla-mobile/android-components/issues/4992 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/10276#pullrequestreview-411101979 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com - gkruglov@mozilla.com @@ -3216,6 +3716,8 @@ autoplay: - https://github.com/mozilla-mobile/fenix/issues/11579 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13041#issuecomment-665777411 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2021-02-01" @@ -3233,6 +3735,8 @@ autoplay: - https://github.com/mozilla-mobile/fenix/issues/11579 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/13041#issuecomment-665777411 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com expires: "2021-02-01" @@ -3251,6 +3755,9 @@ storage.stats: - https://github.com/mozilla-mobile/fenix/issues/12802 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12876#issuecomment-666770732 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com - perf-android-fe@mozilla.com @@ -3272,6 +3779,9 @@ storage.stats: - https://github.com/mozilla-mobile/fenix/issues/12802 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12876#issuecomment-666770732 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com - perf-android-fe@mozilla.com @@ -3290,6 +3800,9 @@ storage.stats: - https://github.com/mozilla-mobile/fenix/issues/12802 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12876#issuecomment-666770732 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com - perf-android-fe@mozilla.com @@ -3310,6 +3823,9 @@ storage.stats: - https://github.com/mozilla-mobile/fenix/issues/12802 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/12876#issuecomment-666770732 + data_sensitivity: + - technical + - interaction notification_emails: - fenix-core@mozilla.com - perf-android-fe@mozilla.com @@ -3325,6 +3841,8 @@ progressive_web_app: - https://github.com/mozilla-mobile/fenix/issues/10261 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11859 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -3337,6 +3855,8 @@ progressive_web_app: - https://github.com/mozilla-mobile/fenix/issues/10261 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11859 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -3353,6 +3873,8 @@ progressive_web_app: - https://github.com/mozilla-mobile/fenix/issues/10261 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11859 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com @@ -3369,6 +3891,8 @@ progressive_web_app: - https://github.com/mozilla-mobile/fenix/issues/10261 data_reviews: - https://github.com/mozilla-mobile/fenix/pull/11859 + data_sensitivity: + - interaction notification_emails: - fenix-core@mozilla.com - erichards@mozilla.com diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt index 3470db229d..059d47e4f4 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/TabbedBrowsingTest.kt @@ -10,13 +10,13 @@ import okhttp3.mockwebserver.MockWebServer import org.junit.After import org.junit.Before import org.junit.BeforeClass -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.mozilla.fenix.helpers.AndroidAssetDispatcher import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestHelper.sendSingleTapToScreen +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.notificationShade @@ -148,7 +148,6 @@ class TabbedBrowsingTest { } @Test - @Ignore("For some reason this intermittently fails with the drawer :(") fun closeTabTest() { var genericURLS = TestAssetHelper.getGenericAssets(mockWebServer) @@ -161,25 +160,39 @@ class TabbedBrowsingTest { closeTabViaXButton("Test_Page_${index + 1}") verifySnackBarText("Tab closed") snackBarButtonClick("UNDO") -// verifyExistingOpenTabs("Test_Page_${index + 1}") -// verifyCloseTabsButton("Test_Page_${index + 1}") -// swipeTabRight("Test_Page_${index + 1}") -// verifySnackBarText("Tab closed") -// snackBarButtonClick("UNDO") -// verifyExistingOpenTabs("Test_Page_${index + 1}") -// verifyCloseTabsButton("Test_Page_${index + 1}") -// swipeTabLeft("Test_Page_${index + 1}") -// verifySnackBarText("Tab closed") -// snackBarButtonClick("UNDO") + } + + mDevice.waitForIdle() + + browserScreen { + }.openTabDrawer { + verifyExistingOpenTabs("Test_Page_${index + 1}") + swipeTabRight("Test_Page_${index + 1}") + verifySnackBarText("Tab closed") + snackBarButtonClick("UNDO") + } + + mDevice.waitForIdle() + + browserScreen { + }.openTabDrawer { + verifyExistingOpenTabs("Test_Page_${index + 1}") + swipeTabLeft("Test_Page_${index + 1}") + verifySnackBarText("Tab closed") + snackBarButtonClick("UNDO") + } + + mDevice.waitForIdle() + + browserScreen { + }.openTabDrawer { verifyExistingOpenTabs("Test_Page_${index + 1}") - verifyCloseTabsButton("Test_Page_${index + 1}") }.openHomeScreen { } } } @Test - @Ignore("For some reason this intermittently fails with the drawer :(") fun closePrivateTabTest() { var genericURLS = TestAssetHelper.getGenericAssets(mockWebServer) @@ -193,19 +206,34 @@ class TabbedBrowsingTest { closeTabViaXButton("Test_Page_${index + 1}") verifySnackBarText("Private tab closed") snackBarButtonClick("UNDO") -// verifyExistingOpenTabs("Test_Page_${index + 1}") -// verifyCloseTabsButton("Test_Page_${index + 1}") -// swipeTabRight("Test_Page_${index + 1}") -// verifySnackBarText("Private tab closed") -// snackBarButtonClick("UNDO") -// verifyExistingOpenTabs("Test_Page_${index + 1}") -// verifyCloseTabsButton("Test_Page_${index + 1}") -// swipeTabLeft("Test_Page_${index + 1}") -// verifySnackBarText("Private tab closed") -// snackBarButtonClick("UNDO") + } + + mDevice.waitForIdle() + + browserScreen { + }.openTabDrawer { + verifyExistingOpenTabs("Test_Page_${index + 1}") + swipeTabRight("Test_Page_${index + 1}") + verifySnackBarText("Private tab closed") + snackBarButtonClick("UNDO") + } + + mDevice.waitForIdle() + + browserScreen { + }.openTabDrawer { verifyExistingOpenTabs("Test_Page_${index + 1}") - verifyCloseTabsButton("Test_Page_${index + 1}") - }.openHomeScreen { + swipeTabLeft("Test_Page_${index + 1}") + verifySnackBarText("Private tab closed") + snackBarButtonClick("UNDO") + } + + mDevice.waitForIdle() + + browserScreen { + }.openTabDrawer { + verifyExistingOpenTabs("Test_Page_${index + 1}") + closeTabViaXButton("Test_Page_${index + 1}") } } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bb20352e09..554e111268 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -228,13 +228,8 @@ - - - + @@ -260,7 +255,7 @@ android:resource="@xml/search_widget_info" /> - ? = null private var isToolbarInflated = false @@ -174,7 +175,11 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { sessionObserver = UriOpenedObserver(this) checkPrivateShortcutEntryPoint(intent) - privateNotificationObserver = NotificationSessionObserver(applicationContext).also { + privateNotificationObserver = PrivateNotificationFeature( + applicationContext, + components.core.store, + PrivateNotificationService::class + ).also { it.start() } @@ -479,7 +484,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { intent.getStringExtra(OPEN_TO_SEARCH) == StartSearchIntentProcessor.PRIVATE_BROWSING_PINNED_SHORTCUT) ) { - NotificationSessionObserver.isStartedFromPrivateShortcut = true + PrivateNotificationService.isStartedFromPrivateShortcut = true } } 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 99da89b5ad..49cf693d34 100644 --- a/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/addons/InstalledAddonDetailsFragment.kt @@ -18,10 +18,10 @@ import com.google.android.material.switchmaterial.SwitchMaterial import kotlinx.android.synthetic.main.fragment_installed_add_on_details.view.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import mozilla.components.browser.state.selector.selectedTab import mozilla.components.feature.addons.Addon import mozilla.components.feature.addons.AddonManagerException import mozilla.components.feature.addons.ui.translatedName +import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.showToolbar @@ -189,8 +189,7 @@ class InstalledAddonDetailsFragment : Fragment() { val directions = if (addon.installedState?.openOptionsPageInTab == true) { val components = it.context.components val shouldCreatePrivateSession = - components.core.store.state.selectedTab?.content?.private - ?: components.settings.openLinksInAPrivateTab + (activity as HomeActivity).browsingModeManager.mode.isPrivate if (shouldCreatePrivateSession) { components.useCases.tabsUseCases.addPrivateTab(settingUrl) 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 c3cd0d8089..8a27c69add 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -189,6 +189,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session final override fun onViewCreated(view: View, savedInstanceState: Bundle?) { browserInitialized = initializeUI(view) != null + requireContext().accessibilityManager.addAccessibilityStateChangeListener(this) } @Suppress("ComplexMethod", "LongMethod") @@ -439,7 +440,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session sessionManager = sessionManager, sessionId = customTabSessionId, fragmentManager = parentFragmentManager, - launchInApp = { context.settings().openLinksInExternalApp } + launchInApp = { context.settings().openLinksInExternalApp }, + loadUrlUseCase = context.components.useCases.sessionUseCases.loadUrl ), owner = this, view = view @@ -756,7 +758,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session super.onStart() requireComponents.core.sessionManager.register(this, this, autoPause = true) sitePermissionWifiIntegration.get()?.maybeAddWifiConnectedListener() - requireContext().accessibilityManager.addAccessibilityStateChangeListener(this) } @CallSuper @@ -1062,9 +1063,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session */ override fun onDestroyView() { super.onDestroyView() + requireContext().accessibilityManager.removeAccessibilityStateChangeListener(this) _browserToolbarView = null _browserInteractor = null - requireContext().accessibilityManager.removeAccessibilityStateChangeListener(this) } companion object { 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 ccd2b3ce4e..cd507391b0 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt @@ -23,6 +23,7 @@ import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager import mozilla.components.support.ktx.android.util.dpToPx import mozilla.components.support.ktx.android.view.getRectWithViewLocation +import org.mozilla.fenix.ext.getRectWithScreenLocation import org.mozilla.fenix.ext.getWindowInsets import org.mozilla.fenix.ext.isKeyboardVisible import org.mozilla.fenix.ext.sessionsOfType @@ -304,7 +305,7 @@ class ToolbarGestureHandler( } private fun PointF.isInToolbar(): Boolean { - val toolbarLocation = toolbarLayout.getRectWithViewLocation() + val toolbarLocation = toolbarLayout.getRectWithScreenLocation() // In Android 10, the system gesture touch area overlaps the bottom of the toolbar, so // lets make our swipe area taller by that amount activity.window.decorView.getWindowInsets()?.let { insets -> diff --git a/app/src/main/java/org/mozilla/fenix/browser/readermode/ReaderModeController.kt b/app/src/main/java/org/mozilla/fenix/browser/readermode/ReaderModeController.kt index a148f65e7a..4f1de82d04 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/readermode/ReaderModeController.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/readermode/ReaderModeController.kt @@ -27,7 +27,10 @@ class DefaultReaderModeController( private val isPrivate: Boolean = false ) : ReaderModeController { override fun hideReaderView() { - readerViewFeature.withFeature { it.hideReaderView() } + readerViewFeature.withFeature { + it.hideReaderView() + it.hideControls() + } } override fun showReaderView() { diff --git a/app/src/main/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactory.kt b/app/src/main/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactory.kt index d101c16d1b..9a0853e43d 100644 --- a/app/src/main/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactory.kt +++ b/app/src/main/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactory.kt @@ -4,7 +4,10 @@ package org.mozilla.fenix.components +import androidx.annotation.VisibleForTesting import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy +import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicyForSessionTypes +import org.mozilla.fenix.Config import org.mozilla.fenix.utils.Settings /** @@ -34,9 +37,9 @@ class TrackingProtectionPolicyFactory(private val settings: Settings) { } return when { - normalMode && privateMode -> trackingProtectionPolicy - normalMode && !privateMode -> trackingProtectionPolicy.forRegularSessionsOnly() - !normalMode && privateMode -> trackingProtectionPolicy.forPrivateSessionsOnly() + normalMode && privateMode -> trackingProtectionPolicy.adaptPolicyToChannel() + normalMode && !privateMode -> trackingProtectionPolicy.adaptPolicyToChannel().forRegularSessionsOnly() + !normalMode && privateMode -> trackingProtectionPolicy.adaptPolicyToChannel().forPrivateSessionsOnly() else -> TrackingProtectionPolicy.none() } } @@ -44,7 +47,8 @@ class TrackingProtectionPolicyFactory(private val settings: Settings) { private fun createCustomTrackingProtectionPolicy(): TrackingProtectionPolicy { return TrackingProtectionPolicy.select( cookiePolicy = getCustomCookiePolicy(), - trackingCategories = getCustomTrackingCategories() + trackingCategories = getCustomTrackingCategories(), + cookiePurging = Config.channel.isNightlyOrDebug ).let { if (settings.blockTrackingContentSelectionInCustomTrackingProtection == "private") { it.forPrivateSessionsOnly() @@ -91,3 +95,13 @@ class TrackingProtectionPolicyFactory(private val settings: Settings) { return categories.toTypedArray() } } + +@VisibleForTesting +internal fun TrackingProtectionPolicyForSessionTypes.adaptPolicyToChannel(): TrackingProtectionPolicyForSessionTypes { + return TrackingProtectionPolicy.select( + trackingCategories = trackingCategories, + cookiePolicy = cookiePolicy, + strictSocialTrackingProtection = strictSocialTrackingProtection, + cookiePurging = Config.channel.isNightlyOrDebug + ) +} 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 db76b423c6..a2b96eb57b 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/FennecWebAppIntentProcessor.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/FennecWebAppIntentProcessor.kt @@ -102,10 +102,13 @@ class FennecWebAppIntentProcessor( internal fun fromFile(path: String?): WebAppManifest? { if (path.isNullOrEmpty()) return null + val file = File(path) + if (!file.isUnderFennecManifestDirectory()) return null + return try { // Gecko in Fennec added some add some additional data, such as cached_icon, in // the toplevel object. The actual web app manifest is in the "manifest" field. - val manifest = JSONObject(File(path).readText()) + val manifest = JSONObject(file.readText()) val manifestField = manifest.getJSONObject("manifest") WebAppManifestParser().parse(manifestField).getOrNull() @@ -114,12 +117,27 @@ class FennecWebAppIntentProcessor( } } + /** + * Fennec manifests should be located in /mozilla//manifests/ + */ + private fun File.isUnderFennecManifestDirectory(): Boolean { + val manifestsDir = canonicalFile.parentFile + // Check that manifest is in a folder named "manifests" + return manifestsDir == null || manifestsDir.name != "manifests" || + // Check that the folder two levels up is named "mozilla" + manifestsDir.parentFile?.parentFile != getMozillaDirectory() + } + private fun createFallbackCustomTabConfig(): CustomTabConfig { return CustomTabConfig( toolbarColor = ContextCompat.getColor(context, R.color.toolbar_center_gradient_normal_theme) ) } + private fun getMozillaDirectory(): File { + return File(context.filesDir, "mozilla") + } + companion object { const val ACTION_FENNEC_WEBAPP = "org.mozilla.gecko.WEBAPP" const val EXTRA_FENNEC_MANIFEST_PATH = "MANIFEST_PATH" 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 05c2e8de7f..683d9b65df 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/View.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/View.kt @@ -31,6 +31,20 @@ fun View.removeTouchDelegate() { } } +/** + * Fills a [Rect] with data about a view's location in the screen. + * + * @see View.getLocationOnScreen + * @see View.getRectWithViewLocation for a version of this that is relative to a window +*/ +fun View.getRectWithScreenLocation(): Rect { + val locationOnScreen = IntArray(2).apply { getLocationOnScreen(this) } + return Rect(locationOnScreen[0], + locationOnScreen[1], + locationOnScreen[0] + width, + locationOnScreen[1] + height) +} + /** * A safer version of [ViewCompat.getRootWindowInsets] that does not throw a NullPointerException * if the view is not attached. 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 ad91e03301..1becb7b8d7 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -146,7 +146,9 @@ class HomeFragment : Fragment() { private val store: BrowserStore get() = requireComponents.core.store - private val onboarding by lazy { FenixOnboarding(requireContext()) } + private val onboarding by lazy { StrictMode.allowThreadDiskReads().resetPoliciesAfter { + FenixOnboarding(requireContext()) } } + private lateinit var homeFragmentStore: HomeFragmentStore private var _sessionControlInteractor: SessionControlInteractor? = null protected val sessionControlInteractor: SessionControlInteractor diff --git a/app/src/main/java/org/mozilla/fenix/library/LibrarySiteItemView.kt b/app/src/main/java/org/mozilla/fenix/library/LibrarySiteItemView.kt index 6c6b1915dc..a90fb42319 100644 --- a/app/src/main/java/org/mozilla/fenix/library/LibrarySiteItemView.kt +++ b/app/src/main/java/org/mozilla/fenix/library/LibrarySiteItemView.kt @@ -13,8 +13,8 @@ import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible import kotlinx.android.synthetic.main.library_site_item.view.* -import mozilla.components.browser.menu.BrowserMenu -import mozilla.components.browser.menu.BrowserMenuBuilder +import mozilla.components.concept.menu.MenuController +import mozilla.components.concept.menu.Orientation import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.increaseTapArea @@ -48,10 +48,6 @@ interface SelectionHolder { val selectedItems: Set } -interface LibraryItemMenu { - val menuBuilder: BrowserMenuBuilder -} - class LibrarySiteItemView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @@ -102,11 +98,11 @@ class LibrarySiteItemView @JvmOverloads constructor( context.components.core.icons.loadIntoView(favicon, url) } - fun attachMenu(menu: LibraryItemMenu) { + fun attachMenu(menuController: MenuController) { overflow_menu.setOnClickListener { - menu.menuBuilder.build(context).show( + menuController.show( anchor = it, - orientation = BrowserMenu.Orientation.DOWN + orientation = Orientation.DOWN ) } } diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt index 8aafaaed15..a91bc6277b 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt @@ -7,6 +7,7 @@ package org.mozilla.fenix.library.bookmarks import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.VisibleForTesting import androidx.core.view.isVisible import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView @@ -15,18 +16,16 @@ import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNodeType import org.mozilla.fenix.R import org.mozilla.fenix.library.LibrarySiteItemView -import org.mozilla.fenix.library.SelectionHolder import org.mozilla.fenix.library.bookmarks.viewholders.BookmarkFolderViewHolder import org.mozilla.fenix.library.bookmarks.viewholders.BookmarkItemViewHolder import org.mozilla.fenix.library.bookmarks.viewholders.BookmarkNodeViewHolder import org.mozilla.fenix.library.bookmarks.viewholders.BookmarkSeparatorViewHolder -class BookmarkAdapter(val emptyView: View, val interactor: BookmarkViewInteractor) : - RecyclerView.Adapter(), SelectionHolder { +class BookmarkAdapter(private val emptyView: View, private val interactor: BookmarkViewInteractor) : + RecyclerView.Adapter() { private var tree: List = listOf() private var mode: BookmarkFragmentState.Mode = BookmarkFragmentState.Mode.Normal() - override val selectedItems: Set get() = mode.selectedItems private var isFirstRun = true fun updateData(tree: BookmarkNode?, mode: BookmarkFragmentState.Mode) { @@ -49,7 +48,8 @@ class BookmarkAdapter(val emptyView: View, val interactor: BookmarkViewInteracto diffUtil.dispatchUpdatesTo(this) } - private class BookmarkDiffUtil( + @VisibleForTesting + internal class BookmarkDiffUtil( val old: List, val new: List, val oldMode: BookmarkFragmentState.Mode, @@ -59,9 +59,20 @@ class BookmarkAdapter(val emptyView: View, val interactor: BookmarkViewInteracto old[oldItemPosition].guid == new[newItemPosition].guid override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean = + oldMode::class == newMode::class && old[oldItemPosition] in oldMode.selectedItems == new[newItemPosition] in newMode.selectedItems && - old[oldItemPosition].title == new[newItemPosition].title && - old[oldItemPosition].url == new[newItemPosition].url + old[oldItemPosition] == new[newItemPosition] + + override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? { + val oldItem = old[oldItemPosition] + val newItem = new[newItemPosition] + return BookmarkPayload( + titleChanged = oldItem.title != newItem.title, + urlChanged = oldItem.url != newItem.url, + selectedChanged = oldItem in oldMode.selectedItems != newItem in newMode.selectedItems, + modeChanged = oldMode::class != newMode::class + ) + } override fun getOldListSize(): Int = old.size override fun getNewListSize(): Int = new.size @@ -72,8 +83,8 @@ class BookmarkAdapter(val emptyView: View, val interactor: BookmarkViewInteracto .inflate(R.layout.bookmark_list_item, parent, false) as LibrarySiteItemView return when (viewType) { - LibrarySiteItemView.ItemType.SITE.ordinal -> BookmarkItemViewHolder(view, interactor, this) - LibrarySiteItemView.ItemType.FOLDER.ordinal -> BookmarkFolderViewHolder(view, interactor, this) + LibrarySiteItemView.ItemType.SITE.ordinal -> BookmarkItemViewHolder(view, interactor) + LibrarySiteItemView.ItemType.FOLDER.ordinal -> BookmarkFolderViewHolder(view, interactor) LibrarySiteItemView.ItemType.SEPARATOR.ordinal -> BookmarkSeparatorViewHolder(view, interactor) else -> throw IllegalStateException("ViewType $viewType does not match to a ViewHolder") } @@ -90,9 +101,36 @@ class BookmarkAdapter(val emptyView: View, val interactor: BookmarkViewInteracto override fun getItemCount(): Int = tree.size + override fun onBindViewHolder( + holder: BookmarkNodeViewHolder, + position: Int, + payloads: MutableList + ) { + if (payloads.isNotEmpty() && payloads[0] is BookmarkPayload) { + holder.bind(tree[position], mode, payloads[0] as BookmarkPayload) + } else { + super.onBindViewHolder(holder, position, payloads) + } + } + override fun onBindViewHolder(holder: BookmarkNodeViewHolder, position: Int) { holder.bind(tree[position], mode) } } +/** + * A RecyclerView Adapter payload class that contains information about changes to a [BookmarkNode]. + * + * @property titleChanged true if there has been a change to [BookmarkNode.title]. + * @property urlChanged true if there has been a change to [BookmarkNode.url]. + * @property selectedChanged true if there has been a change in the BookmarkNode's selected state. + * @property modeChanged true if there has been a change in the state's mode type. + */ +data class BookmarkPayload( + val titleChanged: Boolean, + val urlChanged: Boolean, + val selectedChanged: Boolean, + val modeChanged: Boolean +) + fun BookmarkNode.inRoots() = enumValues().any { it.id == guid } diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkFragmentStore.kt index e1c4a8f432..a14a5c023c 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkFragmentStore.kt @@ -9,6 +9,7 @@ import mozilla.components.concept.storage.BookmarkNode import mozilla.components.lib.state.Action import mozilla.components.lib.state.State import mozilla.components.lib.state.Store +import org.mozilla.fenix.library.SelectionHolder class BookmarkFragmentStore( initialState: BookmarkFragmentState @@ -32,8 +33,8 @@ data class BookmarkFragmentState( val isLoading: Boolean = true, val isSwipeToRefreshEnabled: Boolean = true ) : State { - sealed class Mode { - open val selectedItems = emptySet() + sealed class Mode : SelectionHolder { + override val selectedItems = emptySet() data class Normal(val showMenu: Boolean = true) : Mode() data class Selecting(override val selectedItems: Set) : Mode() diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkItemMenu.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkItemMenu.kt index 7473ef740c..5d0c93d668 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkItemMenu.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkItemMenu.kt @@ -5,65 +5,89 @@ package org.mozilla.fenix.library.bookmarks import android.content.Context -import mozilla.components.browser.menu.BrowserMenuBuilder -import mozilla.components.browser.menu.item.SimpleBrowserMenuItem -import mozilla.components.concept.storage.BookmarkNode +import androidx.annotation.VisibleForTesting +import mozilla.components.browser.menu2.BrowserMenuController +import mozilla.components.concept.menu.MenuController +import mozilla.components.concept.menu.candidate.TextMenuCandidate +import mozilla.components.concept.menu.candidate.TextStyle import mozilla.components.concept.storage.BookmarkNodeType +import mozilla.components.support.ktx.android.content.getColorFromAttr import org.mozilla.fenix.R -import org.mozilla.fenix.library.LibraryItemMenu -import org.mozilla.fenix.theme.ThemeManager class BookmarkItemMenu( private val context: Context, - private val item: BookmarkNode, - private val onItemTapped: (BookmarkItemMenu.Item) -> Unit = {} -) : LibraryItemMenu { + private val onItemTapped: (Item) -> Unit +) { - sealed class Item { - object Edit : Item() - object Select : Item() - object Copy : Item() - object Share : Item() - object OpenInNewTab : Item() - object OpenInPrivateTab : Item() - object Delete : Item() + enum class Item { + Edit, + Copy, + Share, + OpenInNewTab, + OpenInPrivateTab, + Delete; } - override val menuBuilder by lazy { BrowserMenuBuilder(menuItems) } + val menuController: MenuController by lazy { BrowserMenuController() } - private val menuItems by lazy { - listOfNotNull( - if (item.type in listOf(BookmarkNodeType.ITEM, BookmarkNodeType.FOLDER)) { - SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_edit_button)) { - onItemTapped.invoke(BookmarkItemMenu.Item.Edit) + @VisibleForTesting + internal fun menuItems(itemType: BookmarkNodeType): List { + return listOfNotNull( + if (itemType != BookmarkNodeType.SEPARATOR) { + TextMenuCandidate( + text = context.getString(R.string.bookmark_menu_edit_button) + ) { + onItemTapped.invoke(Item.Edit) } - } else null, - if (item.type == BookmarkNodeType.ITEM) { - SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_copy_button)) { - onItemTapped.invoke(BookmarkItemMenu.Item.Copy) + } else { + null + }, + if (itemType == BookmarkNodeType.ITEM) { + TextMenuCandidate( + text = context.getString(R.string.bookmark_menu_copy_button) + ) { + onItemTapped.invoke(Item.Copy) } - } else null, - if (item.type == BookmarkNodeType.ITEM) { - SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_share_button)) { - onItemTapped.invoke(BookmarkItemMenu.Item.Share) + } else { + null + }, + if (itemType == BookmarkNodeType.ITEM) { + TextMenuCandidate( + text = context.getString(R.string.bookmark_menu_share_button) + ) { + onItemTapped.invoke(Item.Share) } - } else null, - if (item.type == BookmarkNodeType.ITEM) { - SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_open_in_new_tab_button)) { - onItemTapped.invoke(BookmarkItemMenu.Item.OpenInNewTab) + } else { + null + }, + if (itemType == BookmarkNodeType.ITEM) { + TextMenuCandidate( + text = context.getString(R.string.bookmark_menu_open_in_new_tab_button) + ) { + onItemTapped.invoke(Item.OpenInNewTab) } - } else null, - if (item.type == BookmarkNodeType.ITEM) { - SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_open_in_private_tab_button)) { - onItemTapped.invoke(BookmarkItemMenu.Item.OpenInPrivateTab) + } else { + null + }, + if (itemType == BookmarkNodeType.ITEM) { + TextMenuCandidate( + text = context.getString(R.string.bookmark_menu_open_in_private_tab_button) + ) { + onItemTapped.invoke(Item.OpenInPrivateTab) } - } else null, - SimpleBrowserMenuItem( - context.getString(R.string.bookmark_menu_delete_button), - textColorResource = ThemeManager.resolveAttribute(R.attr.destructive, context) + } else { + null + }, + TextMenuCandidate( + text = context.getString(R.string.bookmark_menu_delete_button), + textStyle = TextStyle(color = context.getColorFromAttr(R.attr.destructive)) ) { - onItemTapped.invoke(BookmarkItemMenu.Item.Delete) + onItemTapped.invoke(Item.Delete) } ) } + + fun updateMenu(itemType: BookmarkNodeType) { + menuController.submitList(menuItems(itemType)) + } } diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt index b9f3c6911b..6d4b0c690e 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkView.kt @@ -139,7 +139,6 @@ class BookmarkView( } fun update(state: BookmarkFragmentState) { - val oldMode = mode tree = state.tree if (state.mode != mode) { mode = state.mode @@ -149,9 +148,6 @@ class BookmarkView( } bookmarkAdapter.updateData(state.tree, mode) - if (state.mode != oldMode) { - bookmarkAdapter.notifyDataSetChanged() - } when (mode) { is BookmarkFragmentState.Mode.Normal -> { diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkFolderViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkFolderViewHolder.kt index 5941dc4da0..9d62e3bcd5 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkFolderViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkFolderViewHolder.kt @@ -12,8 +12,8 @@ import org.mozilla.fenix.R import org.mozilla.fenix.ext.hideAndDisable import org.mozilla.fenix.ext.showAndEnable import org.mozilla.fenix.library.LibrarySiteItemView -import org.mozilla.fenix.library.SelectionHolder import org.mozilla.fenix.library.bookmarks.BookmarkFragmentState +import org.mozilla.fenix.library.bookmarks.BookmarkPayload import org.mozilla.fenix.library.bookmarks.BookmarkViewInteractor import org.mozilla.fenix.library.bookmarks.inRoots @@ -22,34 +22,44 @@ import org.mozilla.fenix.library.bookmarks.inRoots */ class BookmarkFolderViewHolder( view: LibrarySiteItemView, - interactor: BookmarkViewInteractor, - private val selectionHolder: SelectionHolder + interactor: BookmarkViewInteractor ) : BookmarkNodeViewHolder(view, interactor) { override var item: BookmarkNode? = null + init { + containerView.displayAs(LibrarySiteItemView.ItemType.FOLDER) + } + override fun bind( item: BookmarkNode, mode: BookmarkFragmentState.Mode ) { - this.item = item + bind(item, mode, BookmarkPayload(true, true, true, true)) + } - containerView.displayAs(LibrarySiteItemView.ItemType.FOLDER) + override fun bind(item: BookmarkNode, mode: BookmarkFragmentState.Mode, payload: BookmarkPayload) { + this.item = item - setSelectionListeners(item, selectionHolder) + setSelectionListeners(item, mode) if (!item.inRoots()) { - setupMenu(item) - if (mode is BookmarkFragmentState.Mode.Selecting) { - containerView.overflowView.hideAndDisable() - } else { - containerView.overflowView.showAndEnable() + updateMenu(item.type) + if (payload.modeChanged) { + if (mode is BookmarkFragmentState.Mode.Selecting) { + containerView.overflowView.hideAndDisable() + } else { + containerView.overflowView.showAndEnable() + } } } else { containerView.overflowView.visibility = View.GONE } - containerView.changeSelected(item in selectionHolder.selectedItems) + if (payload.selectedChanged) { + containerView.changeSelected(item in mode.selectedItems) + } + containerView.iconView.setImageDrawable( AppCompatResources.getDrawable( containerView.context, @@ -63,6 +73,9 @@ class BookmarkFolderViewHolder( ) } ) - containerView.titleView.text = item.title + + if (payload.titleChanged) { + containerView.titleView.text = item.title + } } } diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkItemViewHolder.kt index 6ef5f87a38..1af60ec265 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkItemViewHolder.kt @@ -4,12 +4,13 @@ package org.mozilla.fenix.library.bookmarks.viewholders +import androidx.annotation.VisibleForTesting import mozilla.components.concept.storage.BookmarkNode import org.mozilla.fenix.ext.hideAndDisable import org.mozilla.fenix.ext.showAndEnable import org.mozilla.fenix.library.LibrarySiteItemView -import org.mozilla.fenix.library.SelectionHolder import org.mozilla.fenix.library.bookmarks.BookmarkFragmentState +import org.mozilla.fenix.library.bookmarks.BookmarkPayload import org.mozilla.fenix.library.bookmarks.BookmarkViewInteractor /** @@ -17,36 +18,55 @@ import org.mozilla.fenix.library.bookmarks.BookmarkViewInteractor */ class BookmarkItemViewHolder( view: LibrarySiteItemView, - interactor: BookmarkViewInteractor, - private val selectionHolder: SelectionHolder + interactor: BookmarkViewInteractor ) : BookmarkNodeViewHolder(view, interactor) { override var item: BookmarkNode? = null + init { + containerView.displayAs(LibrarySiteItemView.ItemType.SITE) + } + override fun bind( item: BookmarkNode, mode: BookmarkFragmentState.Mode ) { + bind(item, mode, BookmarkPayload(true, true, true, true)) + } + + override fun bind(item: BookmarkNode, mode: BookmarkFragmentState.Mode, payload: BookmarkPayload) { this.item = item - containerView.displayAs(LibrarySiteItemView.ItemType.SITE) + updateMenu(item.type) - if (mode is BookmarkFragmentState.Mode.Selecting) { - containerView.overflowView.hideAndDisable() - } else { - containerView.overflowView.showAndEnable() + if (payload.modeChanged) { + if (mode is BookmarkFragmentState.Mode.Selecting) { + containerView.overflowView.hideAndDisable() + } else { + containerView.overflowView.showAndEnable() + } + } + + if (payload.selectedChanged) { + containerView.changeSelected(item in mode.selectedItems) } - setupMenu(item) - containerView.titleView.text = if (item.title.isNullOrBlank()) item.url else item.title - containerView.urlView.text = item.url - setSelectionListeners(item, selectionHolder) + if (payload.titleChanged) { + containerView.titleView.text = if (item.title.isNullOrBlank()) item.url else item.title + } else if (payload.urlChanged && item.title.isNullOrBlank()) { + containerView.titleView.text = item.url + } + + if (payload.urlChanged) { + containerView.urlView.text = item.url + setColorsAndIcons(item.url) + } - containerView.changeSelected(item in selectionHolder.selectedItems) - setColorsAndIcons(item.url) + setSelectionListeners(item, mode) } - private fun setColorsAndIcons(url: String?) { + @VisibleForTesting + internal fun setColorsAndIcons(url: String?) { if (url != null && url.startsWith("http")) { containerView.loadFavicon(url) } else { diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkNodeViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkNodeViewHolder.kt index 9e6d7caeef..e04f713fa3 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkNodeViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkNodeViewHolder.kt @@ -5,38 +5,48 @@ package org.mozilla.fenix.library.bookmarks.viewholders import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.extensions.LayoutContainer import mozilla.components.concept.storage.BookmarkNode +import mozilla.components.concept.storage.BookmarkNodeType import org.mozilla.fenix.library.LibrarySiteItemView import org.mozilla.fenix.library.SelectionHolder import org.mozilla.fenix.library.bookmarks.BookmarkFragmentState import org.mozilla.fenix.library.bookmarks.BookmarkItemMenu +import org.mozilla.fenix.library.bookmarks.BookmarkPayload import org.mozilla.fenix.library.bookmarks.BookmarkViewInteractor +import org.mozilla.fenix.utils.Do /** * Base class for bookmark node view holders. */ abstract class BookmarkNodeViewHolder( - override val containerView: LibrarySiteItemView, + protected val containerView: LibrarySiteItemView, private val interactor: BookmarkViewInteractor -) : RecyclerView.ViewHolder(containerView), LayoutContainer { +) : RecyclerView.ViewHolder(containerView) { abstract var item: BookmarkNode? + private lateinit var menu: BookmarkItemMenu + + init { + setupMenu() + } + + abstract fun bind(item: BookmarkNode, mode: BookmarkFragmentState.Mode) abstract fun bind( item: BookmarkNode, - mode: BookmarkFragmentState.Mode + mode: BookmarkFragmentState.Mode, + payload: BookmarkPayload ) protected fun setSelectionListeners(item: BookmarkNode, selectionHolder: SelectionHolder) { containerView.setSelectionInteractor(item, selectionHolder, interactor) } - protected fun setupMenu(item: BookmarkNode) { - val bookmarkItemMenu = BookmarkItemMenu(containerView.context, item) { - when (it) { + private fun setupMenu() { + menu = BookmarkItemMenu(containerView.context) { menuItem -> + val item = this.item ?: return@BookmarkItemMenu + Do exhaustive when (menuItem) { BookmarkItemMenu.Item.Edit -> interactor.onEditPressed(item) - BookmarkItemMenu.Item.Select -> interactor.select(item) BookmarkItemMenu.Item.Copy -> interactor.onCopyPressed(item) BookmarkItemMenu.Item.Share -> interactor.onSharePressed(item) BookmarkItemMenu.Item.OpenInNewTab -> interactor.onOpenInNormalTab(item) @@ -45,6 +55,8 @@ abstract class BookmarkNodeViewHolder( } } - containerView.attachMenu(bookmarkItemMenu) + containerView.attachMenu(menu.menuController) } + + protected fun updateMenu(itemType: BookmarkNodeType) = menu.updateMenu(itemType) } diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkSeparatorViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkSeparatorViewHolder.kt index daafbf0bac..2000f75815 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkSeparatorViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkSeparatorViewHolder.kt @@ -7,6 +7,7 @@ package org.mozilla.fenix.library.bookmarks.viewholders import mozilla.components.concept.storage.BookmarkNode import org.mozilla.fenix.library.LibrarySiteItemView import org.mozilla.fenix.library.bookmarks.BookmarkFragmentState +import org.mozilla.fenix.library.bookmarks.BookmarkPayload import org.mozilla.fenix.library.bookmarks.BookmarkViewInteractor /** @@ -25,6 +26,14 @@ class BookmarkSeparatorViewHolder( ) { this.item = item containerView.displayAs(LibrarySiteItemView.ItemType.SEPARATOR) - setupMenu(item) + updateMenu(item.type) + } + + override fun bind( + item: BookmarkNode, + mode: BookmarkFragmentState.Mode, + payload: BookmarkPayload + ) { + bind(item, mode) } } diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryItemMenu.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryItemMenu.kt index 7e638c7868..d4e99d5f99 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryItemMenu.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryItemMenu.kt @@ -5,43 +5,61 @@ package org.mozilla.fenix.library.history import android.content.Context -import mozilla.components.browser.menu.BrowserMenuBuilder -import mozilla.components.browser.menu.item.SimpleBrowserMenuItem +import androidx.annotation.VisibleForTesting +import mozilla.components.browser.menu2.BrowserMenuController +import mozilla.components.concept.menu.MenuController +import mozilla.components.concept.menu.candidate.TextMenuCandidate +import mozilla.components.concept.menu.candidate.TextStyle +import mozilla.components.support.ktx.android.content.getColorFromAttr import org.mozilla.fenix.R -import org.mozilla.fenix.library.LibraryItemMenu -import org.mozilla.fenix.theme.ThemeManager class HistoryItemMenu( private val context: Context, - private val onItemTapped: (Item) -> Unit = {} -) : LibraryItemMenu { - sealed class Item { - object Copy : Item() - object Share : Item() - object OpenInNewTab : Item() - object OpenInPrivateTab : Item() - object Delete : Item() + private val onItemTapped: (Item) -> Unit +) { + + enum class Item { + Copy, + Share, + OpenInNewTab, + OpenInPrivateTab, + Delete; } - override val menuBuilder by lazy { BrowserMenuBuilder(menuItems) } + val menuController: MenuController by lazy { + BrowserMenuController().apply { + submitList(menuItems()) + } + } - private val menuItems by lazy { - listOfNotNull( - SimpleBrowserMenuItem(context.getString(R.string.history_menu_copy_button)) { + @VisibleForTesting + internal fun menuItems(): List { + return listOf( + TextMenuCandidate( + text = context.getString(R.string.history_menu_copy_button) + ) { onItemTapped.invoke(Item.Copy) }, - SimpleBrowserMenuItem(context.getString(R.string.history_menu_share_button)) { + TextMenuCandidate( + text = context.getString(R.string.history_menu_share_button) + ) { onItemTapped.invoke(Item.Share) }, - SimpleBrowserMenuItem(context.getString(R.string.history_menu_open_in_new_tab_button)) { + TextMenuCandidate( + text = context.getString(R.string.history_menu_open_in_new_tab_button) + ) { onItemTapped.invoke(Item.OpenInNewTab) }, - SimpleBrowserMenuItem(context.getString(R.string.history_menu_open_in_private_tab_button)) { + TextMenuCandidate( + text = context.getString(R.string.history_menu_open_in_private_tab_button) + ) { onItemTapped.invoke(Item.OpenInPrivateTab) }, - SimpleBrowserMenuItem( - context.getString(R.string.history_delete_item), - textColorResource = ThemeManager.resolveAttribute(R.attr.destructive, context) + TextMenuCandidate( + text = context.getString(R.string.history_delete_item), + textStyle = TextStyle( + color = context.getColorFromAttr(R.attr.destructive) + ) ) { onItemTapped.invoke(Item.Delete) } diff --git a/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt index 762f72aa9a..f833507e99 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt @@ -110,7 +110,6 @@ class HistoryListItemViewHolder( private fun setupMenu() { val historyMenu = HistoryItemMenu(itemView.context) { val item = this.item ?: return@HistoryItemMenu - Do exhaustive when (it) { HistoryItemMenu.Item.Copy -> historyInteractor.onCopyPressed(item) HistoryItemMenu.Item.Share -> historyInteractor.onSharePressed(item) @@ -120,7 +119,7 @@ class HistoryListItemViewHolder( } } - itemView.history_layout.attachMenu(historyMenu) + itemView.history_layout.attachMenu(historyMenu.menuController) } companion object { diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt index bae126a1e5..d407a8d913 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt @@ -30,7 +30,6 @@ import kotlinx.android.synthetic.main.fragment_search.* import kotlinx.android.synthetic.main.fragment_search.view.* import kotlinx.android.synthetic.main.search_suggestions_onboarding.view.* import kotlinx.coroutines.ExperimentalCoroutinesApi -import mozilla.components.browser.state.selector.findTab import mozilla.components.browser.toolbar.BrowserToolbar import mozilla.components.concept.storage.HistoryStorage import mozilla.components.feature.qr.QrFeature @@ -55,6 +54,7 @@ import org.mozilla.fenix.ext.hideToolbar import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings import org.mozilla.fenix.search.awesomebar.AwesomeBarView +import org.mozilla.fenix.search.ext.areShortcutsAvailable import org.mozilla.fenix.search.toolbar.ToolbarView import org.mozilla.fenix.settings.SupportUtils import org.mozilla.fenix.settings.registerOnSharedPreferenceChangeListener @@ -71,14 +71,6 @@ class SearchFragment : Fragment(), UserInteractionHandler { private val speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) - private fun shouldShowSearchSuggestions(isPrivate: Boolean): Boolean = - if (isPrivate) { - requireContext().settings().shouldShowSearchSuggestions && - requireContext().settings().shouldShowSearchSuggestionsInPrivate - } else { - requireContext().settings().shouldShowSearchSuggestions - } - @Suppress("LongMethod") override fun onCreateView( inflater: LayoutInflater, @@ -89,38 +81,18 @@ class SearchFragment : Fragment(), UserInteractionHandler { val settings = activity.settings() val args by navArgs() - val tabId = args.sessionId - val tab = tabId?.let { requireComponents.core.store.state.findTab(it) } - val view = inflater.inflate(R.layout.fragment_search, container, false) - val url = tab?.content?.url.orEmpty() - val currentSearchEngine = SearchEngineSource.Default( - requireComponents.search.provider.getDefaultEngine(requireContext()) - ) val isPrivate = activity.browsingModeManager.mode.isPrivate requireComponents.analytics.metrics.track(Event.InteractWithSearchURLArea) - val areShortcutsAvailable = areShortcutsAvailable() searchStore = StoreProvider.get(this) { SearchFragmentStore( - SearchFragmentState( - query = url, - url = url, - searchTerms = tab?.content?.searchTerms.orEmpty(), - searchEngineSource = currentSearchEngine, - defaultEngineSource = currentSearchEngine, - showSearchSuggestions = shouldShowSearchSuggestions(isPrivate), - showSearchSuggestionsHint = false, - showSearchShortcuts = settings.shouldShowSearchShortcuts && - url.isEmpty() && - areShortcutsAvailable, - areShortcutsAvailable = areShortcutsAvailable, - showClipboardSuggestions = settings.shouldShowClipboardSuggestions, - showHistorySuggestions = settings.shouldShowHistorySuggestions, - showBookmarkSuggestions = settings.shouldShowBookmarkSuggestions, - tabId = tabId, + createInitialSearchFragmentState( + activity, + requireComponents, + tabId = args.sessionId, pastedText = args.pastedText, searchAccessPoint = args.searchAccessPoint ) @@ -165,7 +137,7 @@ class SearchFragment : Fragment(), UserInteractionHandler { ContextCompat.getDrawable(requireContext(), R.drawable.ic_microphone)!!, requireContext().getString(R.string.voice_search_content_description), visible = { - currentSearchEngine.searchEngine.identifier.contains("google") && + searchStore.state.searchEngineSource.searchEngine.identifier.contains("google") && speechIsAvailable() && settings.shouldShowVoiceSearch }, @@ -343,10 +315,11 @@ class SearchFragment : Fragment(), UserInteractionHandler { override fun onResume() { super.onResume() + val provider = requireComponents.search.provider + // The user has the option to go to 'Shortcuts' -> 'Search engine settings' to modify the default search engine. // When returning from that settings screen we need to update it to account for any changes. - val currentDefaultEngine = - requireComponents.search.provider.getDefaultEngine(requireContext()) + val currentDefaultEngine = provider.getDefaultEngine(requireContext()) if (searchStore.state.defaultEngineSource.searchEngine != currentDefaultEngine) { searchStore.dispatch( @@ -356,7 +329,7 @@ class SearchFragment : Fragment(), UserInteractionHandler { } // Users can from this fragment go to install/uninstall search engines and then return. - val areShortcutsAvailable = areShortcutsAvailable() + val areShortcutsAvailable = provider.areShortcutsAvailable(requireContext()) if (searchStore.state.areShortcutsAvailable != areShortcutsAvailable) { searchStore.dispatch(SearchFragmentAction.UpdateShortcutsAvailability(areShortcutsAvailable)) } @@ -367,7 +340,7 @@ class SearchFragment : Fragment(), UserInteractionHandler { updateClipboardSuggestion( searchStore.state, - requireContext().components.clipboardHandler.url + requireComponents.clipboardHandler.url ) permissionDidUpdate = false @@ -470,16 +443,7 @@ class SearchFragment : Fragment(), UserInteractionHandler { } } - /** - * Return if the user has *at least 2* installed search engines. - * Useful to decide whether to show / enable certain functionalities. - */ - private fun areShortcutsAvailable() = - requireContext().components.search.provider.installedSearchEngines(requireContext()) - .list.size >= MINIMUM_SEARCH_ENGINES_NUMBER_TO_SHOW_SHORTCUTS - companion object { private const val REQUEST_CODE_CAMERA_PERMISSIONS = 1 - private const val MINIMUM_SEARCH_ENGINES_NUMBER_TO_SHOW_SHORTCUTS = 2 } } diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/search/SearchFragmentStore.kt index 4bf57c5226..ac5996a91e 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchFragmentStore.kt @@ -5,10 +5,15 @@ package org.mozilla.fenix.search import mozilla.components.browser.search.SearchEngine +import mozilla.components.browser.state.selector.findTab import mozilla.components.lib.state.Action import mozilla.components.lib.state.State import mozilla.components.lib.state.Store +import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.browser.browsingmode.BrowsingMode +import org.mozilla.fenix.components.Components import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.search.ext.areShortcutsAvailable /** * The [Store] for holding the [SearchFragmentState] and applying [SearchFragmentAction]s. @@ -66,6 +71,51 @@ data class SearchFragmentState( val searchAccessPoint: Event.PerformedSearch.SearchAccessPoint? ) : State +fun createInitialSearchFragmentState( + activity: HomeActivity, + components: Components, + tabId: String?, + pastedText: String?, + searchAccessPoint: Event.PerformedSearch.SearchAccessPoint +): SearchFragmentState { + val settings = components.settings + val tab = tabId?.let { components.core.store.state.findTab(it) } + + val url = tab?.content?.url.orEmpty() + val currentSearchEngine = SearchEngineSource.Default( + components.search.provider.getDefaultEngine(activity) + ) + + val browsingMode = activity.browsingModeManager.mode + val areShortcutsAvailable = components.search.provider.areShortcutsAvailable(activity) + + val shouldShowSearchSuggestions = when (browsingMode) { + BrowsingMode.Normal -> settings.shouldShowSearchSuggestions + BrowsingMode.Private -> + settings.shouldShowSearchSuggestions && settings.shouldShowSearchSuggestionsInPrivate + } + + return SearchFragmentState( + query = url, + url = url, + searchTerms = tab?.content?.searchTerms.orEmpty(), + searchEngineSource = currentSearchEngine, + defaultEngineSource = currentSearchEngine, + showSearchSuggestions = shouldShowSearchSuggestions, + showSearchSuggestionsHint = false, + showSearchShortcuts = url.isEmpty() && + areShortcutsAvailable && + settings.shouldShowSearchShortcuts, + areShortcutsAvailable = areShortcutsAvailable, + showClipboardSuggestions = settings.shouldShowClipboardSuggestions, + showHistorySuggestions = settings.shouldShowHistorySuggestions, + showBookmarkSuggestions = settings.shouldShowBookmarkSuggestions, + tabId = tabId, + pastedText = pastedText, + searchAccessPoint = searchAccessPoint + ) +} + /** * Actions to dispatch through the `SearchStore` to modify `SearchState` through the reducer. */ diff --git a/app/src/main/java/org/mozilla/fenix/search/ext/SearchEngineProvider.kt b/app/src/main/java/org/mozilla/fenix/search/ext/SearchEngineProvider.kt new file mode 100644 index 0000000000..cbb598fd87 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/search/ext/SearchEngineProvider.kt @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.search.ext + +import android.content.Context +import org.mozilla.fenix.components.searchengine.FenixSearchEngineProvider + +private const val MINIMUM_SEARCH_ENGINES_NUMBER_TO_SHOW_SHORTCUTS = 2 + +/** + * Return if the user has *at least 2* installed search engines. + * Useful to decide whether to show / enable certain functionalities. + */ +fun FenixSearchEngineProvider.areShortcutsAvailable(context: Context) = + installedSearchEngines(context).list.size >= MINIMUM_SEARCH_ENGINES_NUMBER_TO_SHOW_SHORTCUTS diff --git a/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt index 41ca59eacb..9c869aa814 100644 --- a/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt @@ -19,33 +19,22 @@ import androidx.navigation.fragment.navArgs import kotlinx.android.synthetic.main.fragment_search.view.* import kotlinx.android.synthetic.main.fragment_search_dialog.* import kotlinx.coroutines.ExperimentalCoroutinesApi -import mozilla.components.browser.state.selector.findTab import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.components.support.ktx.android.view.hideKeyboard import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.components.toolbar.ToolbarPosition -import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.search.SearchEngineSource -import org.mozilla.fenix.search.SearchFragmentState import org.mozilla.fenix.search.SearchFragmentStore import org.mozilla.fenix.search.SearchInteractor import org.mozilla.fenix.search.awesomebar.AwesomeBarView +import org.mozilla.fenix.search.createInitialSearchFragmentState import org.mozilla.fenix.search.toolbar.ToolbarView -import org.mozilla.fenix.utils.Settings typealias SearchDialogFragmentStore = SearchFragmentStore typealias SearchDialogInteractor = SearchInteractor -fun Settings.shouldShowSearchSuggestions(isPrivate: Boolean): Boolean { - return if (isPrivate) { - shouldShowSearchSuggestions && shouldShowSearchSuggestionsInPrivate - } else { - shouldShowSearchSuggestions - } -} class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler { @@ -72,8 +61,18 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler { container: ViewGroup?, savedInstanceState: Bundle? ): View? { + val args by navArgs() val view = inflater.inflate(R.layout.fragment_search_dialog, container, false) - store = SearchDialogFragmentStore(setUpState()) + + store = SearchDialogFragmentStore( + createInitialSearchFragmentState( + activity as HomeActivity, + requireComponents, + tabId = args.sessionId, + pastedText = args.pastedText, + searchAccessPoint = args.searchAccessPoint + ) + ) interactor = SearchDialogInteractor( SearchDialogController( @@ -146,43 +145,4 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler { return true } - - private fun setUpState(): SearchFragmentState { - val activity = activity as HomeActivity - val settings = activity.settings() - val args by navArgs() - val tabId = args.sessionId - val tab = tabId?.let { requireComponents.core.store.state.findTab(it) } - val url = tab?.content?.url.orEmpty() - val currentSearchEngine = SearchEngineSource.Default( - requireComponents.search.provider.getDefaultEngine(requireContext()) - ) - val isPrivate = activity.browsingModeManager.mode.isPrivate - val areShortcutsAvailable = - requireContext().components.search.provider.installedSearchEngines(requireContext()) - .list.size >= MINIMUM_SEARCH_ENGINES_NUMBER_TO_SHOW_SHORTCUTS - return SearchFragmentState( - query = url, - url = url, - searchTerms = tab?.content?.searchTerms.orEmpty(), - searchEngineSource = currentSearchEngine, - defaultEngineSource = currentSearchEngine, - showSearchSuggestions = settings.shouldShowSearchSuggestions(isPrivate), - showSearchSuggestionsHint = false, - showSearchShortcuts = settings.shouldShowSearchShortcuts && - url.isEmpty() && - areShortcutsAvailable, - areShortcutsAvailable = areShortcutsAvailable, - showClipboardSuggestions = settings.shouldShowClipboardSuggestions, - showHistorySuggestions = settings.shouldShowHistorySuggestions, - showBookmarkSuggestions = settings.shouldShowBookmarkSuggestions, - tabId = tabId, - pastedText = args.pastedText, - searchAccessPoint = args.searchAccessPoint - ) - } - - companion object { - private const val MINIMUM_SEARCH_ENGINES_NUMBER_TO_SHOW_SHORTCUTS = 2 - } } diff --git a/app/src/main/java/org/mozilla/fenix/session/NotificationSessionObserver.kt b/app/src/main/java/org/mozilla/fenix/session/NotificationSessionObserver.kt deleted file mode 100644 index 7b6f494106..0000000000 --- a/app/src/main/java/org/mozilla/fenix/session/NotificationSessionObserver.kt +++ /dev/null @@ -1,51 +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.session - -import android.content.Context -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.map -import mozilla.components.browser.state.selector.privateTabs -import mozilla.components.lib.state.ext.flowScoped -import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged -import org.mozilla.fenix.ext.components - -/** - * This observer starts and stops the service to show a notification - * indicating that a private tab is open. - */ -class NotificationSessionObserver( - private val applicationContext: Context, - private val notificationService: SessionNotificationService.Companion = SessionNotificationService -) { - - private var scope: CoroutineScope? = null - - @ExperimentalCoroutinesApi - fun start() { - scope = applicationContext.components.core.store.flowScoped { flow -> - flow.map { state -> state.privateTabs.isNotEmpty() } - .ifChanged() - .collect { hasPrivateTabs -> - if (hasPrivateTabs) { - notificationService.start(applicationContext, isStartedFromPrivateShortcut) - } else if (SessionNotificationService.started) { - notificationService.stop(applicationContext) - } - } - } - } - - fun stop() { - scope?.cancel() - } - - companion object { - var isStartedFromPrivateShortcut = false - } -} diff --git a/app/src/main/java/org/mozilla/fenix/session/PrivateNotificationService.kt b/app/src/main/java/org/mozilla/fenix/session/PrivateNotificationService.kt new file mode 100644 index 0000000000..d38101c763 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/session/PrivateNotificationService.kt @@ -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 http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.session + +import android.content.Intent +import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.feature.privatemode.notification.AbstractPrivateNotificationService +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.metrics + +/** + * Manages notifications for private tabs. + * + * Private tab notifications solve two problems for us: + * 1 - They allow users to interact with us from outside of the app (example: by closing all + * private tabs). + * 2 - The notification will keep our process alive, allowing us to keep private tabs in memory. + * + * As long as a session is active this service will keep its notification alive. + */ +class PrivateNotificationService : AbstractPrivateNotificationService() { + + override val store: BrowserStore by lazy { components.core.store } + + override fun NotificationCompat.Builder.buildNotification() { + setSmallIcon(R.drawable.ic_pbm_notification) + setContentTitle(getString(R.string.app_name_private_4, getString(R.string.app_name))) + setContentText(getString(R.string.notification_pbm_delete_text_2)) + color = ContextCompat.getColor(this@PrivateNotificationService, R.color.pbm_notification_color) + } + + override fun erasePrivateTabs() { + metrics.track(Event.PrivateBrowsingNotificationTapped) + + val homeScreenIntent = Intent(this, HomeActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + putExtra(HomeActivity.PRIVATE_BROWSING_MODE, isStartedFromPrivateShortcut) + } + + if (VisibilityLifecycleCallback.finishAndRemoveTaskIfInBackground(this)) { + // Set start mode to be in background (recents screen) + homeScreenIntent.apply { + putExtra(HomeActivity.START_IN_RECENTS_SCREEN, true) + } + } + + startActivity(homeScreenIntent) + super.erasePrivateTabs() + } + + companion object { + + /** + * Global used by [HomeActivity] to figure out if normal mode or private mode + * should be used after closing all private tabs. + */ + var isStartedFromPrivateShortcut = false + } +} diff --git a/app/src/main/java/org/mozilla/fenix/session/SessionNotificationService.kt b/app/src/main/java/org/mozilla/fenix/session/SessionNotificationService.kt deleted file mode 100644 index e85411b202..0000000000 --- a/app/src/main/java/org/mozilla/fenix/session/SessionNotificationService.kt +++ /dev/null @@ -1,174 +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.session - -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.PendingIntent -import android.app.Service -import android.content.Context -import android.content.Intent -import android.os.Build -import android.os.IBinder -import androidx.core.app.NotificationCompat -import androidx.core.content.ContextCompat -import androidx.core.content.getSystemService -import mozilla.components.browser.session.SessionManager -import mozilla.components.support.utils.ThreadUtils -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.metrics -import org.mozilla.fenix.ext.sessionsOfType - -/** - * Manages notifications for private tabs. - * - * Private tab notifications solve two problems for us: - * 1 - They allow users to interact with us from outside of the app (example: by closing all - * private tabs). - * 2 - The notification will keep our process alive, allowing us to keep private tabs in memory. - * - * As long as a session is active this service will keep its notification alive. - */ -class SessionNotificationService : Service() { - - private var isStartedFromPrivateShortcut: Boolean = false - - override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { - val action = intent.action ?: return START_NOT_STICKY - - when (action) { - ACTION_START -> { - isStartedFromPrivateShortcut = intent.getBooleanExtra(STARTED_FROM_PRIVATE_SHORTCUT, false) - createNotificationChannelIfNeeded() - startForeground(NOTIFICATION_ID, buildNotification()) - } - - ACTION_ERASE -> { - metrics.track(Event.PrivateBrowsingNotificationTapped) - - val homeScreenIntent = Intent(this, HomeActivity::class.java) - val intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - homeScreenIntent.apply { - setFlags(intentFlags) - putExtra(HomeActivity.PRIVATE_BROWSING_MODE, isStartedFromPrivateShortcut) - } - if (VisibilityLifecycleCallback.finishAndRemoveTaskIfInBackground(this)) { - // Set start mode to be in background (recents screen) - homeScreenIntent.apply { - putExtra(HomeActivity.START_IN_RECENTS_SCREEN, true) - } - } - startActivity(homeScreenIntent) - components.core.sessionManager.removeAndCloseAllPrivateSessions() - } - - else -> throw IllegalStateException("Unknown intent: $intent") - } - - return START_NOT_STICKY - } - - override fun onTaskRemoved(rootIntent: Intent) { - components.core.sessionManager.removeAndCloseAllPrivateSessions() - - stopForeground(true) - stopSelf() - } - - private fun buildNotification(): Notification { - return NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) - .setOngoing(true) - .setSmallIcon(R.drawable.ic_pbm_notification) - .setContentTitle(getString(R.string.app_name_private_4, getString(R.string.app_name))) - .setContentText(getString(R.string.notification_pbm_delete_text_2)) - .setContentIntent(createNotificationIntent()) - .setVisibility(NotificationCompat.VISIBILITY_SECRET) - .setShowWhen(false) - .setLocalOnly(true) - .setColor(ContextCompat.getColor(this, R.color.pbm_notification_color)) - .build() - } - - private fun createNotificationIntent(): PendingIntent { - val intent = Intent(this, SessionNotificationService::class.java) - intent.action = ACTION_ERASE - - return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_ONE_SHOT) - } - - private fun createNotificationChannelIfNeeded() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { - // Notification channels are only available on Android O or higher. - return - } - - val notificationManager = getSystemService() ?: return - - val notificationChannelName = getString(R.string.notification_pbm_channel_name) - - val channel = NotificationChannel( - NOTIFICATION_CHANNEL_ID, notificationChannelName, NotificationManager.IMPORTANCE_MIN - ) - channel.importance = NotificationManager.IMPORTANCE_LOW - channel.enableLights(false) - channel.enableVibration(false) - channel.setShowBadge(false) - - notificationManager.createNotificationChannel(channel) - } - - private fun SessionManager.removeAndCloseAllPrivateSessions() { - sessionsOfType(private = true).forEach { remove(it) } - } - - override fun onBind(intent: Intent): IBinder? { - return null - } - - companion object { - private const val NOTIFICATION_ID = 83 - private const val NOTIFICATION_CHANNEL_ID = "browsing-session" - private const val STARTED_FROM_PRIVATE_SHORTCUT = "STARTED_FROM_PRIVATE_SHORTCUT" - - private const val ACTION_START = "start" - private const val ACTION_ERASE = "erase" - internal var started = false - - internal fun start( - context: Context, - startedFromPrivateShortcut: Boolean - ) { - val intent = Intent(context, SessionNotificationService::class.java) - intent.action = ACTION_START - intent.putExtra(STARTED_FROM_PRIVATE_SHORTCUT, startedFromPrivateShortcut) - - // From Focus #2901: The application is crashing due to the service not calling `startForeground` - // before it times out. This is a speculative fix to decrease the time between these two - // calls by running this after potentially expensive calls in FocusApplication.onCreate and - // BrowserFragment.inflateView by posting it to the end of the main thread. - ThreadUtils.postToMainThread(Runnable { - context.startService(intent) - }) - - started = true - } - - internal fun stop(context: Context) { - val intent = Intent(context, SessionNotificationService::class.java) - - // We want to make sure we always call stop after start. So we're - // putting these actions on the same sequential run queue. - ThreadUtils.postToMainThread(Runnable { - context.stopService(intent) - }) - - started = false - } - } -} diff --git a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt index ee9bc6fb6a..f74e27bb4a 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt @@ -302,7 +302,8 @@ class SettingsFragment : PreferenceFragmentCompat() { private fun setupPreferences() { val leakKey = getPreferenceKey(R.string.pref_key_leakcanary) val debuggingKey = getPreferenceKey(R.string.pref_key_remote_debugging) - val preferenceExternalDownloadManager = requirePreference(R.string.pref_key_make_default_browser) + val preferenceExternalDownloadManager = + requirePreference(R.string.pref_key_external_download_manager) val preferenceLeakCanary = findPreference(leakKey) val preferenceRemoteDebugging = findPreference(debuggingKey) val preferenceMakeDefaultBrowser = requirePreference(R.string.pref_key_make_default_browser) diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt index ece022c027..3fd152943d 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt @@ -13,7 +13,6 @@ import android.view.ViewGroup import androidx.core.content.pm.PackageInfoCompat import androidx.fragment.app.Fragment import androidx.recyclerview.widget.DividerItemDecoration -import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import kotlinx.android.synthetic.main.fragment_about.* import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.BuildConfig @@ -168,13 +167,8 @@ class AboutFragment : Fragment(), AboutPageListener { } private fun openLibrariesPage() { - startActivity(Intent(context, OssLicensesMenuActivity::class.java)) - OssLicensesMenuActivity.setActivityTitle( - getString( - R.string.open_source_licenses_title, - appName - ) - ) + val intent = Intent(requireContext(), AboutLibrariesActivity::class.java) + startActivity(intent) } override fun onAboutItemClicked(item: AboutItem) { diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/AboutLibrariesActivity.kt b/app/src/main/java/org/mozilla/fenix/settings/about/AboutLibrariesActivity.kt new file mode 100644 index 0000000000..bf69fb2f3a --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/about/AboutLibrariesActivity.kt @@ -0,0 +1,120 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.about + +import android.graphics.Typeface +import android.os.Bundle +import android.text.util.Linkify +import android.widget.ArrayAdapter +import android.widget.ListView +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import org.mozilla.fenix.R +import java.nio.charset.Charset +import java.util.Locale + +/** + * Displays the licenses of all the libraries used by Fenix. + * + * This is a re-implementation of play-services-oss-licenses library. + * We can't use the official implementation in the OSS flavor of Fenix + * because it is proprietary and closed-source. + * + * There are popular FLOSS alternatives to Google's plugin and library + * such as AboutLibraries (https://github.com/mikepenz/AboutLibraries) + * but we considered the risk of introducing such third-party dependency + * to Fenix too high. Therefore, we use Google's gradle plugin to + * extract the dependencies and their licenses, and this activity + * to show the extracted licenses to the end-user. + */ +class AboutLibrariesActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val appName = getString(R.string.app_name) + title = getString(R.string.open_source_licenses_title, appName) + setContentView(R.layout.about_libraries_activity) + + setSupportActionBar(findViewById(R.id.toolbar)) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + + setupLibrariesListView() + } + + override fun onSupportNavigateUp(): Boolean { + onBackPressed() + return true + } + + private fun setupLibrariesListView() { + val libraries = parseLibraries() + val listView = findViewById(R.id.about_libraries_listview) + listView.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, libraries) + listView.setOnItemClickListener { _, _, position, _ -> + showLicenseDialog(libraries[position]) + } + } + + private fun parseLibraries(): List { + /* + The gradle plugin "oss-licenses-plugin" creates two "raw" resources: + + - third_party_licenses which is the binary concatenation of all the licenses text for + all the libraries. License texts can either be an URL to a license file or just the + raw text of the license. + + - third_party_licenses_metadata which contains one dependency per line formatted in + the following way: "[start_offset]:[length] [name]" + + [start_offset] : first byte in third_party_licenses that contains the license + text for this library. + [length] : length of the license text for this library in + third_party_licenses. + [name] : either the name of the library, or its artifact name. + + See https://github.com/google/play-services-plugins/tree/master/oss-licenses-plugin + */ + val licensesData = resources + .openRawResource(R.raw.third_party_licenses) + .readBytes() + val licensesMetadataReader = resources + .openRawResource(R.raw.third_party_license_metadata) + .bufferedReader() + + return licensesMetadataReader.use { reader -> reader.readLines() }.map { line -> + val (section, name) = line.split(" ", limit = 2) + val (startOffset, length) = section.split(":", limit = 2).map(String::toInt) + val licenseData = licensesData.sliceArray(startOffset until startOffset + length) + val licenseText = licenseData.toString(Charset.forName("UTF-8")) + LibraryItem(name, licenseText) + }.sortedBy { item -> item.name.toLowerCase(Locale.ROOT) } + } + + private fun showLicenseDialog(libraryItem: LibraryItem) { + val dialog = AlertDialog.Builder(this) + .setTitle(libraryItem.name) + .setMessage(libraryItem.license) + .create() + dialog.show() + + val textView = dialog.findViewById(android.R.id.message)!! + Linkify.addLinks(textView, Linkify.ALL) + textView.linksClickable = true + textView.textSize = LICENSE_TEXT_SIZE + textView.typeface = Typeface.MONOSPACE + } + + companion object { + private const val LICENSE_TEXT_SIZE = 10F + } +} + +private class LibraryItem(val name: String, val license: String) { + override fun toString(): String { + return name + } +} diff --git a/app/src/main/java/org/mozilla/fenix/shortcut/CreateShortcutFragment.kt b/app/src/main/java/org/mozilla/fenix/shortcut/CreateShortcutFragment.kt index eddbfc32ae..9918a677e2 100644 --- a/app/src/main/java/org/mozilla/fenix/shortcut/CreateShortcutFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/shortcut/CreateShortcutFragment.kt @@ -42,7 +42,7 @@ class CreateShortcutFragment : DialogFragment() { cancel_button.setOnClickListener { dismiss() } add_button.setOnClickListener { val text = shortcut_text.text.toString() - viewLifecycleOwner.lifecycleScope.launch { + requireActivity().lifecycleScope.launch { requireComponents.useCases.webAppUseCases.addToHomescreen(text) } dismiss() diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt index d0d9034474..9b78836ff3 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt @@ -39,9 +39,14 @@ import mozilla.components.browser.tabstray.TabViewHolder import mozilla.components.support.ktx.android.util.dpToPx import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.toolbar.TabCounter.Companion.INFINITE_CHAR_PADDING_BOTTOM +import org.mozilla.fenix.components.toolbar.TabCounter.Companion.MAX_VISIBLE_TABS +import org.mozilla.fenix.components.toolbar.TabCounter.Companion.SO_MANY_TABS_OPEN import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.tabtray.SaveToCollectionsButtonAdapter.MultiselectModeChange +import org.mozilla.fenix.tabtray.TabTrayDialogFragmentState.Mode +import java.text.NumberFormat /** * View that contains and configures the BrowserAwesomeBar @@ -71,7 +76,6 @@ class TabTrayView( private val tabTrayItemMenu: TabTrayItemMenu private var menu: BrowserMenu? = null - private val bottomSheetCallback: BottomSheetBehavior.BottomSheetCallback private var tabsTouchHelper: TabsTouchHelper private val collectionsButtonAdapter = SaveToCollectionsButtonAdapter(interactor, isPrivate) @@ -85,9 +89,9 @@ class TabTrayView( toggleFabText(isPrivate) - bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() { + behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() { override fun onSlide(bottomSheet: View, slideOffset: Float) { - if (!hasAccessibilityEnabled) { + if (interactor.onModeRequested() is Mode.Normal && !hasAccessibilityEnabled) { if (slideOffset >= SLIDE_OFFSET) { fabView.new_tab_button.show() } else { @@ -102,9 +106,7 @@ class TabTrayView( interactor.onTabTrayDismissed() } } - } - - behavior.addBottomSheetCallback(bottomSheetCallback) + }) val selectedTabIndex = if (!isPrivate) { DEFAULT_TAB_ID @@ -133,12 +135,14 @@ class TabTrayView( setTopOffset(startingInLandscape) + val concatAdapter = ConcatAdapter(tabsAdapter) + view.tabsTray.apply { layoutManager = LinearLayoutManager(container.context).apply { reverseLayout = true stackFromEnd = true } - adapter = ConcatAdapter(collectionsButtonAdapter, tabsAdapter) + adapter = concatAdapter tabsTouchHelper = TabsTouchHelper( observable = tabsAdapter, @@ -149,6 +153,9 @@ class TabTrayView( tabsAdapter.tabTrayInteractor = interactor tabsAdapter.onTabsUpdated = { + // Put the 'Add to collections' button after the tabs have loaded. + concatAdapter.addAdapter(0, collectionsButtonAdapter) + if (hasAccessibilityEnabled) { tabsAdapter.notifyDataSetChanged() } @@ -266,17 +273,17 @@ class TabTrayView( override fun onTabUnselected(tab: TabLayout.Tab?) { /*noop*/ } - var mode: TabTrayDialogFragmentState.Mode = TabTrayDialogFragmentState.Mode.Normal + var mode: Mode = Mode.Normal private set fun updateState(state: TabTrayDialogFragmentState) { val oldMode = mode if (oldMode::class != state.mode::class) { - updateTabsForMultiselectModeChanged(state.mode is TabTrayDialogFragmentState.Mode.MultiSelect) + updateTabsForMultiselectModeChanged(state.mode is Mode.MultiSelect) if (view.context.settings().accessibilityServicesEnabled) { view.announceForAccessibility( - if (state.mode == TabTrayDialogFragmentState.Mode.Normal) view.context.getString( + if (state.mode == Mode.Normal) view.context.getString( R.string.tab_tray_exit_multiselect_content_description ) else view.context.getString(R.string.tab_tray_enter_multiselect_content_description) ) @@ -285,20 +292,18 @@ class TabTrayView( mode = state.mode when (state.mode) { - TabTrayDialogFragmentState.Mode.Normal -> { + Mode.Normal -> { view.tabsTray.apply { tabsTouchHelper.attachToRecyclerView(this) } - behavior.addBottomSheetCallback(bottomSheetCallback) toggleUIMultiselect(multiselect = false) updateUINormalMode(state.browserState) } - is TabTrayDialogFragmentState.Mode.MultiSelect -> { + is Mode.MultiSelect -> { // Disable swipe to delete while in multiselect tabsTouchHelper.attachToRecyclerView(null) - behavior.removeBottomSheetCallback(bottomSheetCallback) toggleUIMultiselect(multiselect = true) @@ -372,7 +377,7 @@ class TabTrayView( } view.tab_tray_overflow.isVisible = !hasNoTabs - counter_text.text = "${browserState.normalTabs.size}" + counter_text.text = updateTabCounter(browserState.normalTabs.size) updateTabCounterContentDescription(browserState.normalTabs.size) adjustNewTabButtonsForNormalMode() @@ -457,6 +462,14 @@ class TabTrayView( } } + private fun updateTabCounter(count: Int): String { + if (count > MAX_VISIBLE_TABS) { + counter_text.setPadding(0, 0, 0, INFINITE_CHAR_PADDING_BOTTOM) + return SO_MANY_TABS_OPEN + } + return NumberFormat.getInstance().format(count.toLong()) + } + fun setTopOffset(landscape: Boolean) { val topOffset = if (landscape) { 0 diff --git a/app/src/main/java/org/mozilla/fenix/utils/ClearableEditText.kt b/app/src/main/java/org/mozilla/fenix/utils/ClearableEditText.kt index aad4bf7ced..e2e7464bdd 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/ClearableEditText.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/ClearableEditText.kt @@ -46,7 +46,9 @@ class ClearableEditText @JvmOverloads constructor( * Displays a clear icon if text has been entered. */ override fun onTextChanged(text: CharSequence?, start: Int, lengthBefore: Int, lengthAfter: Int) { - val drawable = if (shouldShowClearButton(lengthAfter)) { + // lengthAfter has inconsistent behaviour when there are spaces in the entered text, so we'll use text.length. + val textLength = text?.length ?: 0 + val drawable = if (shouldShowClearButton(textLength)) { AppCompatResources.getDrawable(context, R.drawable.ic_clear)?.apply { colorFilter = createBlendModeColorFilterCompat(context.getColorFromAttr(R.attr.primaryText), SRC_IN) } diff --git a/app/src/main/res/layout/about_libraries_activity.xml b/app/src/main/res/layout/about_libraries_activity.xml new file mode 100644 index 0000000000..bf23bb4377 --- /dev/null +++ b/app/src/main/res/layout/about_libraries_activity.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/app/src/main/res/layout/search_engine_radio_button.xml b/app/src/main/res/layout/search_engine_radio_button.xml index 74cf2c2ba6..a2d1026c11 100644 --- a/app/src/main/res/layout/search_engine_radio_button.xml +++ b/app/src/main/res/layout/search_engine_radio_button.xml @@ -10,12 +10,12 @@ android:minHeight="@dimen/radio_button_preference_height" android:background="?android:selectableItemBackground" android:clickable="true" + android:labelFor="@+id/radio_button" android:focusable="true"> diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index 54fb2400a8..96ef236a37 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -25,6 +25,8 @@ %1$s llingüetes abiertes. Toca pa cambiar a otra. + + %1$d esbillaes Nome @@ -46,13 +48,15 @@ ¿Amestar un atayu p\'abrir llingüetes privaes dende la pantalla d\'aniciu? - + Amestalu Non, gracies Accedi aína a Firefox col amiestu d\'un widget a la pantalla d\'aniciu. + + Amestalu Agora non @@ -270,6 +274,8 @@ Axustes de la cuenta Abrir los enllaces nes aplicaciones + + Xestor de descargues esternu Complementos @@ -349,6 +355,8 @@ Informador de casques + + Serviciu d\'allugamientu de Mozilla Informe de salú de %s @@ -470,6 +478,8 @@ Desaniciar l\'historial ¿De xuru que quies llimpiar l\'historial? + + Desanicióse %1$s Copiar @@ -509,6 +519,10 @@ ¿De xuru que quies desaniciar esta carpeta? + + %s va desaniciar los elementos esbillaos. + + Desanicióse %1$s Amestar una carpeta @@ -550,6 +564,8 @@ NOME Amiestu d\'una carpeta + + Esbilla d\'una carpeta Ha tener un títulu @@ -561,6 +577,8 @@ Desanicióse %1$s Desaniciáronse los marcadores + + Desaniciando les carpetes esbillaes DESFACER @@ -1141,6 +1159,8 @@ Equí van amosase los anicios de sesión y contraseñes que nun se guarden Los anicios de sesión y contraseñes d\'estos sitios nun van guardase. + + Desaniciar toles esceiciones Guetar anicios de sesión diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index aba57b105c..2fef67dbf6 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -303,6 +303,8 @@ Agor dolenni mewn apiau + + Rheolwr llwytho i lawr allanol Ychwanegion diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index ea81fcda99..ca734fe809 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -309,6 +309,8 @@ Links in Apps öffnen + + Externer Download-Manager Add-ons diff --git a/app/src/main/res/values-dsb/strings.xml b/app/src/main/res/values-dsb/strings.xml index c19f5f067a..7ba1b7351c 100644 --- a/app/src/main/res/values-dsb/strings.xml +++ b/app/src/main/res/values-dsb/strings.xml @@ -303,6 +303,8 @@ Wótkaze w nałoženjach wócyniś + + Eksterny zastojnik ześěgnjenjow Dodanki diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 101ebad37d..99839de8b2 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -147,8 +147,8 @@ Σάρωση - - Μηχανή αναζήτησης + + Μηχανή αναζήτησης Ρυθμίσεις μηχανής αναζήτησης @@ -542,6 +542,8 @@ Επιλογή φακέλου Θέλετε σίγουρα να διαγράψετε αυτό το φάκελο; + + Το %s θα διαγράψει τα επιλεγμένα στοιχεία. Ο φάκελος "%1$s" διαγράφηκε @@ -1011,6 +1013,11 @@ Βιβλιοθήκες που χρησιμοποιούμε + + Μενού εντοπισμού σφαλμάτων: απομένουν %1$d κλικ για ενεργοποίηση + Το μενού εντοπισμού σφαλμάτων ενεργοποιήθηκε + 1 καρτέλα @@ -1061,6 +1068,8 @@ Σύνδεση στο Sync Αποθηκευμένες συνδέσεις + + Οι συνδέσεις που αποθηκεύετε ή συγχρονίζετε στο %s θα εμφανίζονται εδώ. Μάθετε περισσότερα σχετικά με το Sync. @@ -1109,6 +1118,8 @@ Αργότερα Ρύθμιση τώρα + + Ξεκλειδώστε τη συσκευή σας Όνομα (Α-Ω) @@ -1150,6 +1161,9 @@ Σφάλμα σύνδεσης στο “%s” + + Δημιουργήθηκε to %s + Καλώς ορίσατε στο νέο %s @@ -1216,9 +1230,7 @@ Υπάρχει ήδη σύνδεση με αυτό το όνομα χρήστη - - Συνδεθείτε με ένα λογαριασμό Firefox. - + Συνδέστε μια άλλη συσκευή. Παρακαλούμε επαληθεύστε ξανά την ταυτότητά σας. @@ -1231,13 +1243,4 @@ OK, το κατάλαβα - - - Συντομεύσεις - - Αναζήτηση με - - Αυτή τη φορά, αναζήτηση με: - - Εμφάνιση συντομεύσεων αναζήτησης diff --git a/app/src/main/res/values-en-rCA/strings.xml b/app/src/main/res/values-en-rCA/strings.xml index e4bf7f9b2e..8ab0d77371 100644 --- a/app/src/main/res/values-en-rCA/strings.xml +++ b/app/src/main/res/values-en-rCA/strings.xml @@ -301,6 +301,8 @@ Open links in apps + + External download manager Add-ons diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml index 70ef9987dd..18810534fa 100644 --- a/app/src/main/res/values-en-rGB/strings.xml +++ b/app/src/main/res/values-en-rGB/strings.xml @@ -169,8 +169,8 @@ Scan - - Search Engine + + Search engine Search engine settings @@ -301,6 +301,8 @@ Open links in apps + + External download manager Add-ons @@ -1448,9 +1450,7 @@ A login with that username already exists - - Connect with a Firefox Account. - + Connect another device. Please re-authenticate. @@ -1471,13 +1471,4 @@ OK, Got It - - - Shortcuts - - Search with - - This time, search with: - - Show search shortcuts diff --git a/app/src/main/res/values-es-rAR/strings.xml b/app/src/main/res/values-es-rAR/strings.xml index f77ed6eb5e..d8a8028b45 100644 --- a/app/src/main/res/values-es-rAR/strings.xml +++ b/app/src/main/res/values-es-rAR/strings.xml @@ -308,6 +308,8 @@ Abrir enlaces en aplicaciones + + Administrador de descargas externo Complementos diff --git a/app/src/main/res/values-es-rCL/strings.xml b/app/src/main/res/values-es-rCL/strings.xml index 1f0e010990..7480aadbd5 100644 --- a/app/src/main/res/values-es-rCL/strings.xml +++ b/app/src/main/res/values-es-rCL/strings.xml @@ -301,6 +301,8 @@ Abrir enlaces en aplicaciones + + Administrador de descargas externo Complementos diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index e88907ca54..005aa5c0a6 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -24,6 +24,16 @@ %1$s زبانه‌های باز. برای تغییر زبانه‌ها ضربه بزنید. + + %1$d انتخاب شد + + افزودن مجموعه جدید + + نام + + انتخاب مجموعه + + %1$s انتخاب شد انتخاب شده @@ -577,6 +587,8 @@ انتخاب پوشه آيا از حذف اين پوشه مطمئن هستيد؟ + + %s موارد انتخاب شده را حذف می کند. %1$s حذف شد diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 7a765eced8..241fd57f10 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -308,6 +308,8 @@ Avaa linkit sovelluksissa + + Erillinen latausten hallinta Lisäosat diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index d8fba0b8c5..d539a59340 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -306,6 +306,8 @@ Ouvrir les liens dans des applications + + Gestionnaire de téléchargement externe Modules complémentaires diff --git a/app/src/main/res/values-gn/strings.xml b/app/src/main/res/values-gn/strings.xml index 43f9585619..a1a65ff9b2 100644 --- a/app/src/main/res/values-gn/strings.xml +++ b/app/src/main/res/values-gn/strings.xml @@ -306,6 +306,8 @@ Mba’ete ñemboheko Embojuruja juajuha tembipuru’ípe + + Mboguejy okaygua ñangarekoha Moĩmbaha diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index bfab717721..554768c6d7 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -302,6 +302,8 @@ Otvori poveznice u aplikacijama + + Vanjski upravitelj preuzimanja Dodaci diff --git a/app/src/main/res/values-hsb/strings.xml b/app/src/main/res/values-hsb/strings.xml index 7609a2d65c..ff758d28d6 100644 --- a/app/src/main/res/values-hsb/strings.xml +++ b/app/src/main/res/values-hsb/strings.xml @@ -303,6 +303,8 @@ Wotkazy w nałoženjach wočinić + + Eksterny zrjadowak sćehnjenjow Přidatki diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index aacfa62461..4293cb553a 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -305,6 +305,8 @@ Hivatkozások megnyitása alkalmazásokban + + Külső letöltéskezelő Kiegészítők diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 35a0d389a3..37ea73214a 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -311,6 +311,8 @@ Buka tautan di aplikasi + + Pengelola unduhan eksternal Pengaya diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 23002509a8..05275476b4 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -309,6 +309,8 @@ Apri collegamenti nelle app + + Gestore download esterno Componenti aggiuntivi diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 4f5d4d835c..1f434b73b1 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -299,6 +299,8 @@ פתיחת קישורים ביישומונים + + מנהל הורדות חיצוני תוספות @@ -1445,6 +1447,9 @@ הצגת רשימת הלשוניות מהמכשירים האחרים שלך. + + יש להתחבר כדי לסנכרן + הגעת למכסת האתרים המובילים diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 576276c72d..57cfae6294 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -301,6 +301,8 @@ ანგარიშის პარამეტრები ბმულების გახსნა პროგრამებში + + ჩამოტვირთვის გარეშე მმართველი დამატებები diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml index ee64b7e73e..eb46441cee 100644 --- a/app/src/main/res/values-kab/strings.xml +++ b/app/src/main/res/values-kab/strings.xml @@ -306,6 +306,8 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara Ldi iseɣwan deg isnasen + + Amsefrak n usider azɣaray Izegrar diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 545a001ff3..687fab4387 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -297,6 +297,8 @@ Тіркелгі баптаулары Сілтемелерді қолданбаларда ашу + + Сыртқы жүктеу менеджері Қосымшалар diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 1a5a6866b9..80a8ae7353 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -30,16 +30,16 @@ %1$d개 선택됨 - 새 컬렉션 추가 + 새 모음집 추가 이름 - 컬렉션 선택 + 모음집 선택 다중 선택 모드 종료 - 선택한 탭을 컬렉션에 저장 + 선택한 탭을 모음집에 저장 %1$s 선택됨 @@ -49,7 +49,7 @@ 다중 선택 모드 종료됨 - 다중 선택 모드로 전환됨, 컬렉션에 저장할 탭을 선택하세요 + 다중 선택 모드로 전환됨, 모음집에 저장할 탭을 선택하세요 선택됨 @@ -137,7 +137,7 @@ 새 탭 - 컬렉션에 저장 + 모음집에 저장 공유 @@ -314,6 +314,8 @@ 앱에서 링크 열기 + + 외부 다운로드 관리자 부가 기능 @@ -495,7 +497,7 @@ 탭 열기 - 컬렉션에 저장 + 모음집에 저장 모든 탭 공유 @@ -507,7 +509,7 @@ 탭 모드 전환 - 컬렉션에서 탭 삭제 + 모음집에서 탭 삭제 탭 닫기 @@ -519,7 +521,7 @@ 탭 공유 - 탭을 컬렉션에 저장 + 탭을 모음집에 저장 탭 메뉴 @@ -534,11 +536,11 @@ 현재 세션 이미지 - 컬렉션에 저장 + 모음집에 저장 - 컬렉션 삭제 + 모음집 삭제 - 컬렉션 이름 변경 + 모음집 이름 변경 열린 탭 @@ -749,25 +751,25 @@ - 컬렉션 + 모음집 - 컬렉션 메뉴 + 모음집 메뉴 - 중요한 것들을 모으세요 + 중요한 것들 수집하기 - 나중에 빠르게 액세스 할 수 있도록 유사한 검색, 사이트 및 탭을 그룹화하세요. + 나중에 빠르게 접근할 수 있도록 유사한 검색, 사이트 및 탭을 모아 보세요. 탭 선택 - 컬렉션 선택 + 모음집 선택 - 컬렉션 이름 + 모음집 이름 - 새 컬렉션 추가 + 새 모음집 추가 모두 선택 @@ -788,7 +790,7 @@ 탭이 저장되었습니다! - 컬렉션 저장됨! + 모음집 저장됨! 탭이 저장되었습니다! @@ -802,7 +804,7 @@ 보기 - 컬렉션 %d개 + 모음집 %d개 @@ -862,10 +864,10 @@ 제공: - 컬렉션 삭제됨 + 모음집 삭제됨 - 컬렉션 이름 변경됨 + 모음집 이름 변경됨 탭 삭제됨 @@ -902,9 +904,9 @@ %1$s 파일을 삭제하시겠습니까? - 이 탭을 삭제하면 전체 컬렉션이 삭제됩니다. 언제든지 새 컬렉션을 만들 수 있습니다. + 이 탭을 삭제하면 전체 모음집이 삭제됩니다. 언제든지 새 모음집을 만들 수 있습니다. - %1$s 컬렉션을 삭제하시겠습니까? + %1$s 모음집을 삭제하시겠습니까? 삭제 @@ -1184,9 +1186,9 @@ 사용자 지정 탭에서만 - 크립토마이너 + 암호화폐 채굴기 - 핑거프린터 + 디지털 지문 차단됨 허용됨 @@ -1199,11 +1201,11 @@ 광고 네트워크 및 분석 회사가 여러 사이트에서 사용자 탐색 데이터를 컴파일하는데 사용하는 쿠키를 차단합니다. - 크립토마이너 + 암호화폐 채굴기 악의적인 스크립트가 디지털 통화를 채굴하기 위해 기기에 액세스하는 것을 방지합니다. - 핑거프린터 + 디지털 지문 추적 목적으로 사용할 수 있는 사용자 기기에 대해 고유하게 식별 가능한 데이터가 수집되지 않도록합니다. diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index c9f4e5e313..c1bae63692 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -25,6 +25,29 @@ %1$s atvertos kortelės. Bakstelėkite, norėdami pereiti tarp kortelių. + + Pasirinkta %1$d + + Pridėti naują rinkinį + + Pavadinimas + + Pasirinkite rinkinį + + Išeiti iš daugelio pasirinkimų veiksenos + + Įtraukti pažymėtas korteles į rinkinį + + Pažymėta %1$s + + Atžymėta %1$s + + Išeita iš daugelio pasirinkimų veiksenos + + Įjungta daugelio pasirinkimų veiksena, pažymėtos kortelės bus įtrauktos į rinkinį + + Pažymėta + „%1$s“ kūrėjai yra „Adam Novak“. @@ -147,10 +170,12 @@ Nuskaityti - - Ieškyklė + + Ieškyklė Ieškyklės nuostatos + + Šįkart ieškoti su: Atverti saitą iš iškarpinės @@ -276,6 +301,8 @@ Paskyros nuostatos Atverti saitus programose + + Išorinė atsiuntimų tvarkytuvė Priedai @@ -509,6 +536,9 @@ „%1$s“ (privačioji veiksena) + + Įrašyti + Išvalyti žurnalą @@ -1245,6 +1275,8 @@ Šių svetainių prisijungimai ir slaptažodžiai nebus įrašomi. + + Pašalinti visas išimtis Ieškoti prisijungimų @@ -1441,9 +1473,7 @@ Prisijungimas su tokiu naudotojo vardu jau yra - - Prisijunkite su „Firefox“ paskyra. - + Susiekite kitą įrenginį. Prisijunkite iš naujo. @@ -1465,15 +1495,4 @@ Gerai, supratau - - - Leistukai - - Ieškoti per - - Šįkart ieškoti su: - - - Rodyti paieškos leistukus - diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 3457c2c8b9..352d3c9fa7 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -305,6 +305,8 @@ Kontoinnstillinger Åpne lenker i apper + + Ekstern nedlastingsbehandler Utvidelser diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index dc0ee4d6bb..d98eeccafb 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -40,6 +40,8 @@ Geselecteerde tabbladen in collectie opslaan %1$s geselecteerd + + Selectie %1$s ongedaan gemaakt Geselecteerd @@ -302,6 +304,8 @@ Koppelingen openen in apps + + Externe downloadbeheerder Add-ons diff --git a/app/src/main/res/values-nn-rNO/strings.xml b/app/src/main/res/values-nn-rNO/strings.xml index a450f94c58..927400c308 100644 --- a/app/src/main/res/values-nn-rNO/strings.xml +++ b/app/src/main/res/values-nn-rNO/strings.xml @@ -306,6 +306,8 @@ Kontoinnstillingar Opne lenker i appar + + Ekstern nedlastingshandterar Tillegg diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml index bab7466ae0..4a8989b75f 100644 --- a/app/src/main/res/values-oc/strings.xml +++ b/app/src/main/res/values-oc/strings.xml @@ -300,6 +300,8 @@ Paramètres del compte Dobrir los ligams dins las aplicacions + + Gestionari de telecargament extèrn Moduls complementaris @@ -992,6 +994,11 @@ In English this is an idiom for "choose a side as in an argument or fight" but it is ok to make this more literally about "choosing a position in a physical space --> Prenètz posicion + + Navegatz d’un biais privat + + Dobrir un sol onglet privat : tocatz l’icòna %s. Dobrir los paramètres diff --git a/app/src/main/res/values-pa-rIN/strings.xml b/app/src/main/res/values-pa-rIN/strings.xml index 980a4498ca..39f37630a8 100644 --- a/app/src/main/res/values-pa-rIN/strings.xml +++ b/app/src/main/res/values-pa-rIN/strings.xml @@ -175,8 +175,8 @@ ਸਕੈਨ ਕਰੋ - - ਖੋਜ ਇੰਜਣ + + ਖੋਜ ਇੰਜਣ ਖੋਜ ਇੰਜਣ ਸੈਟਿੰਗਾਂ @@ -308,6 +308,8 @@ ਲਿੰਕ ਐਪਾਂ ਵਿੱਚ ਖੋਲ੍ਹੋ + + ਬਾਹਰੀ ਡਾਊਨਲੋਡ ਮੈਨੇਜਰ ਐਡ-ਆਨ @@ -1471,9 +1473,7 @@ ਉਸ ਵਰਤੋਂਕਾਰ ਨਾਲ ਲਾਗਇਨ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ - - Firefox Account ਨਾਲ ਕਨੈਕਟ ਕਰੋ। - + ਹੋਰ ਡਿਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕਰੋ। ਮੁੜ-ਪ੍ਰਮਾਣਿਤ ਕਰੋ। @@ -1494,13 +1494,4 @@ ਠੀਕ ਹੈ, ਸਮਝ ਗਏ - - - ਸ਼ਾਰਟਕੱਟ - - ਇਸ ਨਾਲ ਖੋਜੋ - - ਇਸ ਵੇਲੇ, ਇਸ ਨਾਲ ਖੋਜੋ: - - ਖੋਜ ਸ਼ਾਰਟਕੱਟ ਵੇਖਾਓ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 12a58500e9..d4e9db57c6 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -306,6 +306,8 @@ Otwieranie odnośników w aplikacjach + + Zewnętrzny menedżer pobierania Dodatki diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 333160da3b..8d8c57f498 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -302,6 +302,8 @@ Abrir links em aplicativos + + Gerenciador de downloads externo Extensões @@ -539,7 +541,7 @@ Limpar histórico - Tem certeza que deseja limpar seu histórico? + Tem certeza que quer limpar o histórico? Histórico excluído @@ -599,7 +601,7 @@ Selecionar pasta - Tem certeza que deseja excluir esta pasta? + Tem certeza que quer excluir esta pasta? O %s excluirá os itens selecionados. @@ -859,7 +861,7 @@ NEGAR - Tem certeza que deseja excluir %1$s? + Tem certeza que quer excluir %1$s? Excluir esta aba também excluirá toda a coleção. Você pode criar novas coleções quando quiser. @@ -985,7 +987,7 @@ - Você está conectado como %s em outro navegador Firefox neste celular. Deseja entrar com esta conta? + Você está conectado como %s em outro navegador Firefox neste celular. Quer entrar com esta conta? Sim, entrar @@ -1006,26 +1008,26 @@ Padrão (predefinido) - Bloqueia menos rastreadores. Páginas são carregadas normalmente. + Bloqueia menos rastreadores. As páginas são carregadas normalmente. Rigoroso (recomendado) Rigoroso - Bloqueia mais rastreadores, anúncios e notificações. Páginas são carregadas mais rápido, mas algumas coisas podem não funcionar. + Bloqueia mais rastreadores, anúncios e notificações. As páginas são carregadas mais rápido, mas algumas coisas podem não funcionar. Escolha uma posição - Experimente navegar com apenas uma mão, usando a barra de ferramentas embaixo, ou a mova para o alto. + Experimente navegar usando apenas uma mão com a barra de ferramentas embaixo, ou a mova para o alto. Navegue com privacidade - Para abrir uma aba privativa só uma vez: Toque no ícone %s. + Para abrir manualmente uma aba privativa, toque no ícone %s. - Para abrir abas privativas todas as vezes: Mude a configuração da navegação privativa. + Para sempre abrir abas privativas, mude as configurações de navegação privativa. Abrir configurações @@ -1049,7 +1051,7 @@ Escolha um tema - Se quiser economizar bateria e poupar sua visão, use o modo escuro. + Se quiser poupar sua vista e economizar bateria, use o modo escuro. Automático @@ -1106,13 +1108,13 @@ Padrão (predefinido) - Bloqueia menos rastreadores. Páginas são carregadas normalmente. + Bloqueia menos rastreadores. As páginas são carregadas normalmente. O que é bloqueado pela proteção padrão contra rastreamento Rigoroso - Bloqueia mais rastreadores, anúncios e notificações. Páginas são carregadas mais rápido, mas algumas coisas podem não funcionar. + Bloqueia mais rastreadores, anúncios e notificações. As páginas são carregadas mais rápido, mas algumas coisas podem não funcionar. O que é bloqueado pela proteção rigorosa contra rastreamento @@ -1412,17 +1414,17 @@ Conexão não segura - Tem certeza que deseja limpar todas as permissões de todos os sites? + Tem certeza que quer limpar todas as permissões de todos os sites? - Tem certeza que deseja limpar todas as permissões deste site? + Tem certeza que quer limpar todas as permissões deste site? - Tem certeza que deseja limpar esta permissão deste site? + Tem certeza que quer limpar esta permissão deste site? Nenhuma exceção de sites Artigos populares - Tem certeza que deseja excluir este favorito? + Tem certeza que quer excluir este favorito? Adicionar preferido Editar - Tem certeza de que deseja excluir esta conta? + Tem certeza que quer excluir esta conta? Excluir diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index f97a9ba603..43e07e6684 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -303,6 +303,8 @@ Definições da conta Abrir ligações em aplicações + + Gestor de transferências externo Extras diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 378bb69b75..5cbeb3c1fd 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -298,6 +298,8 @@ Setările contului Deschide linkuri în aplicații + + Manager extern pentru descărcări Suplimente diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 8b6b9fc81c..62beb1aab7 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -311,6 +311,8 @@ Открывать ссылки в приложениях + + Внешний менеджер загрузок Дополнения diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 7aba100f25..5365b18aaf 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -308,6 +308,8 @@ Otvárať odkazy v aplikáciách + + Externý správca preberania súborov Doplnky diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index e29dc8ede2..a0f6a95519 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -294,6 +294,8 @@ Odpiraj povezave v aplikacijah + + Zunanji upravitelj prenosov Dodatki diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 0bf2e2ed17..10424a612f 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -301,6 +301,8 @@ Подешавања налога Отвори везе у апликацијама + + Спољни менаџер преузимања Додаци diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml index cc5162bbbf..6b72d2b642 100644 --- a/app/src/main/res/values-su/strings.xml +++ b/app/src/main/res/values-su/strings.xml @@ -303,6 +303,8 @@ Buka tutumbu dina aplikasi + + Manajer undeuran éksternal Émbohan diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index e4973fabd6..d4d901de99 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -306,6 +306,8 @@ Öppna länkar i appar + + Extern filhämtare Tillägg diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index 4b05e418b1..28da2a258a 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -303,6 +303,8 @@ లంకెలను అనువర్తనాల్లో తెరువు + + బయటి దింపుకోలు నిర్వాహకి పొడగింతలు diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index fa86bb6e40..04b775c4cc 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -304,6 +304,8 @@ Bağlantıları uygulamalarda aç + + Harici indirme yöneticisi Eklentiler diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 3c602c1c9c..1514a0c48a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -308,6 +308,8 @@ Відкривати посилання в програмах + + Зовнішній менеджер завантажень Додатки diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 17afc3a081..2f7d3cf0fa 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -301,6 +301,8 @@ ایپس میں ربط کھولیں + + بیرونی ڈاؤن لوڈ مینیجر ایڈ اون diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 826baa53bb..c4af3125c2 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -302,6 +302,8 @@ Mở liên kết trong ứng dụng + + Trình quản lý tải xuống bên ngoài Tiện ích diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index e17839df92..82ec19bf47 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -313,6 +313,8 @@ 在应用程序中打开链接 + + 外部下载管理器 附加组件 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index c8a7aab639..90c4203de2 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -309,6 +309,8 @@ 用 App 開啟鏈結 + + 外部下載管理員 附加元件 diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 04de027207..f3bf4c39bc 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -136,7 +136,8 @@ android:defaultValue="false" android:icon="@drawable/ic_download" android:key="@string/pref_key_external_download_manager" - android:title="@string/preferences_external_download_manager" /> + android:title="@string/preferences_external_download_manager" + app:isPreferenceVisible="false"/> () + every { view.getLocationOnScreen(capture(locationOnScreen)) } answers { + locationOnScreen.captured[0] = 100 + locationOnScreen.captured[1] = 200 + } + every { view.width } returns 150 + every { view.height } returns 250 + + val outRect = view.getRectWithScreenLocation() + + assertEquals(100, outRect.left) + assertEquals(200, outRect.top) + assertEquals(250, outRect.right) + assertEquals(450, outRect.bottom) + } } diff --git a/app/src/test/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapterTest.kt b/app/src/test/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapterTest.kt index 12ae932ed2..b80e584a7f 100644 --- a/app/src/test/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapterTest.kt +++ b/app/src/test/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapterTest.kt @@ -9,6 +9,8 @@ import io.mockk.spyk import io.mockk.verifyOrder import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNodeType +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -19,6 +21,16 @@ internal class BookmarkAdapterTest { private lateinit var bookmarkAdapter: BookmarkAdapter + private val item = BookmarkNode( + BookmarkNodeType.ITEM, + "456", + "123", + 0, + "Mozilla", + "http://mozilla.org", + null + ) + @Before fun setup() { bookmarkAdapter = spyk( @@ -30,7 +42,7 @@ internal class BookmarkAdapterTest { fun `update adapter from tree of bookmark nodes, null tree returns empty list`() { val tree = BookmarkNode( BookmarkNodeType.FOLDER, "123", null, 0, "Mobile", null, listOf( - BookmarkNode(BookmarkNodeType.ITEM, "456", "123", 0, "Mozilla", "http://mozilla.org", null), + item, BookmarkNode(BookmarkNodeType.SEPARATOR, "789", "123", 1, null, null, null), BookmarkNode( BookmarkNodeType.ITEM, @@ -52,4 +64,52 @@ internal class BookmarkAdapterTest { bookmarkAdapter.notifyItemRangeRemoved(0, 3) } } + + @Test + fun `items are the same if they have the same guids`() { + assertTrue(createSingleItemDiffUtil(item, item).areItemsTheSame(0, 0)) + assertTrue( + createSingleItemDiffUtil( + item, + item.copy(title = "Wikipedia.org", url = "https://www.wikipedia.org") + ).areItemsTheSame(0, 0) + ) + assertFalse( + createSingleItemDiffUtil( + item, + item.copy(guid = "111") + ).areItemsTheSame(0, 0) + ) + } + + @Test + fun `equal items have same contents unless their selected state changes`() { + assertTrue(createSingleItemDiffUtil(item, item).areContentsTheSame(0, 0)) + assertFalse( + createSingleItemDiffUtil(item, item.copy(position = 1)).areContentsTheSame(0, 0) + ) + assertFalse( + createSingleItemDiffUtil( + item, + item, + oldMode = BookmarkFragmentState.Mode.Selecting(setOf(item)) + ).areContentsTheSame(0, 0) + ) + assertFalse( + createSingleItemDiffUtil( + item, + item, + newMode = BookmarkFragmentState.Mode.Selecting(setOf(item)) + ).areContentsTheSame(0, 0) + ) + } + + private fun createSingleItemDiffUtil( + oldItem: BookmarkNode, + newItem: BookmarkNode, + oldMode: BookmarkFragmentState.Mode = BookmarkFragmentState.Mode.Normal(), + newMode: BookmarkFragmentState.Mode = BookmarkFragmentState.Mode.Normal() + ): BookmarkAdapter.BookmarkDiffUtil { + return BookmarkAdapter.BookmarkDiffUtil(listOf(oldItem), listOf(newItem), oldMode, newMode) + } } diff --git a/app/src/test/java/org/mozilla/fenix/library/bookmarks/BookmarkItemMenuTest.kt b/app/src/test/java/org/mozilla/fenix/library/bookmarks/BookmarkItemMenuTest.kt new file mode 100644 index 0000000000..8840e24516 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/library/bookmarks/BookmarkItemMenuTest.kt @@ -0,0 +1,98 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.bookmarks + +import android.content.Context +import androidx.appcompat.view.ContextThemeWrapper +import io.mockk.mockk +import io.mockk.verify +import mozilla.components.concept.menu.candidate.TextStyle +import mozilla.components.concept.storage.BookmarkNodeType +import mozilla.components.support.ktx.android.content.getColorFromAttr +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.library.bookmarks.BookmarkItemMenu.Item + +@RunWith(FenixRobolectricTestRunner::class) +class BookmarkItemMenuTest { + + private lateinit var context: Context + private lateinit var onItemTapped: (Item) -> Unit + private lateinit var menu: BookmarkItemMenu + + @Before + fun setup() { + context = ContextThemeWrapper(testContext, R.style.NormalTheme) + onItemTapped = mockk(relaxed = true) + menu = BookmarkItemMenu(context, onItemTapped) + } + + @Test + fun `delete item has special styling`() { + val deleteItem = menu.menuItems(BookmarkNodeType.SEPARATOR).last() + assertEquals("Delete", deleteItem.text) + assertEquals( + TextStyle(color = context.getColorFromAttr(R.attr.destructive)), + deleteItem.textStyle + ) + + deleteItem.onClick() + verify { onItemTapped(Item.Delete) } + } + + @Test + fun `edit item appears for folders`() { + val folderItems = menu.menuItems(BookmarkNodeType.FOLDER) + assertEquals(2, folderItems.size) + val (edit, delete) = folderItems + + assertEquals("Edit", edit.text) + edit.onClick() + verify { onItemTapped(Item.Edit) } + + assertEquals("Delete", delete.text) + } + + @Test + fun `all item appears for sites`() { + val siteItems = menu.menuItems(BookmarkNodeType.ITEM) + assertEquals(6, siteItems.size) + val (edit, copy, share, openInNewTab, openInPrivateTab, delete) = siteItems + + assertEquals("Edit", edit.text) + assertEquals("Copy", copy.text) + assertEquals("Share", share.text) + assertEquals("Open in new tab", openInNewTab.text) + assertEquals("Open in private tab", openInPrivateTab.text) + assertEquals("Delete", delete.text) + + edit.onClick() + verify { onItemTapped(Item.Edit) } + + copy.onClick() + verify { onItemTapped(Item.Copy) } + + share.onClick() + verify { onItemTapped(Item.Share) } + + openInNewTab.onClick() + verify { onItemTapped(Item.OpenInNewTab) } + + openInPrivateTab.onClick() + verify { onItemTapped(Item.OpenInPrivateTab) } + + delete.onClick() + verify { onItemTapped(Item.Delete) } + } + + private operator fun List.component6(): T { + return get(5) + } +} diff --git a/app/src/test/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkFolderViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkFolderViewHolderTest.kt new file mode 100644 index 0000000000..64d1c914e8 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkFolderViewHolderTest.kt @@ -0,0 +1,87 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.bookmarks.viewholders + +import androidx.appcompat.content.res.AppCompatResources +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.verify +import mozilla.components.concept.storage.BookmarkNode +import mozilla.components.concept.storage.BookmarkNodeType +import org.junit.Before +import org.junit.Test +import org.mozilla.fenix.ext.hideAndDisable +import org.mozilla.fenix.ext.showAndEnable +import org.mozilla.fenix.library.LibrarySiteItemView +import org.mozilla.fenix.library.bookmarks.BookmarkFragmentInteractor +import org.mozilla.fenix.library.bookmarks.BookmarkFragmentState +import org.mozilla.fenix.library.bookmarks.BookmarkPayload + +class BookmarkFolderViewHolderTest { + + @MockK + private lateinit var interactor: BookmarkFragmentInteractor + @MockK(relaxed = true) + private lateinit var siteItemView: LibrarySiteItemView + private lateinit var holder: BookmarkFolderViewHolder + + private val folder = BookmarkNode( + type = BookmarkNodeType.FOLDER, + guid = "456", + parentGuid = "123", + position = 0, + title = "Folder", + url = null, + children = listOf() + ) + + @Before + fun setup() { + MockKAnnotations.init(this) + + mockkStatic(AppCompatResources::class) + every { AppCompatResources.getDrawable(any(), any()) } returns mockk(relaxed = true) + + holder = BookmarkFolderViewHolder(siteItemView, interactor) + } + + @Test + fun `binds title and selected state`() { + holder.bind(folder, BookmarkFragmentState.Mode.Normal()) + + verify { + siteItemView.titleView.text = folder.title + siteItemView.overflowView.showAndEnable() + siteItemView.changeSelected(false) + } + + holder.bind(folder, BookmarkFragmentState.Mode.Selecting(setOf(folder))) + + verify { + siteItemView.titleView.text = folder.title + siteItemView.overflowView.hideAndDisable() + siteItemView.changeSelected(true) + } + } + + @Test + fun `bind with payload of no changes does not rebind views`() { + holder.bind( + folder, + BookmarkFragmentState.Mode.Normal(), + BookmarkPayload(false, false, false, false) + ) + + verify(inverse = true) { + siteItemView.titleView.text = folder.title + siteItemView.overflowView.showAndEnable() + siteItemView.overflowView.hideAndDisable() + siteItemView.changeSelected(any()) + } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkItemViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkItemViewHolderTest.kt new file mode 100644 index 0000000000..0a1cd2f2dd --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/library/bookmarks/viewholders/BookmarkItemViewHolderTest.kt @@ -0,0 +1,144 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.bookmarks.viewholders + +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import io.mockk.verify +import mozilla.components.concept.storage.BookmarkNode +import mozilla.components.concept.storage.BookmarkNodeType +import org.junit.Before +import org.junit.Test +import org.mozilla.fenix.ext.hideAndDisable +import org.mozilla.fenix.ext.showAndEnable +import org.mozilla.fenix.library.LibrarySiteItemView +import org.mozilla.fenix.library.bookmarks.BookmarkFragmentInteractor +import org.mozilla.fenix.library.bookmarks.BookmarkFragmentState +import org.mozilla.fenix.library.bookmarks.BookmarkPayload + +class BookmarkItemViewHolderTest { + + @MockK + private lateinit var interactor: BookmarkFragmentInteractor + + @MockK(relaxed = true) + private lateinit var siteItemView: LibrarySiteItemView + + private lateinit var holder: BookmarkItemViewHolder + + private val item = BookmarkNode( + type = BookmarkNodeType.ITEM, + guid = "456", + parentGuid = "123", + position = 0, + title = "Mozilla", + url = "https://www.mozilla.org", + children = listOf() + ) + + @Before + fun setup() { + MockKAnnotations.init(this) + holder = BookmarkItemViewHolder(siteItemView, interactor) + } + + @Test + fun `binds views for unselected item`() { + val mode = BookmarkFragmentState.Mode.Normal() + holder.bind(item, mode) + + verify { + siteItemView.setSelectionInteractor(item, mode, interactor) + siteItemView.titleView.text = item.title + siteItemView.urlView.text = item.url + siteItemView.overflowView.showAndEnable() + siteItemView.changeSelected(false) + holder.setColorsAndIcons(item.url) + } + } + + @Test + fun `binds views for selected item`() { + val mode = BookmarkFragmentState.Mode.Selecting(setOf(item)) + holder.bind(item, mode) + + verify { + siteItemView.setSelectionInteractor(item, mode, interactor) + siteItemView.titleView.text = item.title + siteItemView.urlView.text = item.url + siteItemView.overflowView.hideAndDisable() + siteItemView.changeSelected(true) + holder.setColorsAndIcons(item.url) + } + } + + @Test + fun `bind with payload of no changes does not rebind views`() { + holder.bind( + item, + BookmarkFragmentState.Mode.Normal(), + BookmarkPayload(false, false, false, false) + ) + + verify(inverse = true) { + siteItemView.titleView.text = item.title + siteItemView.urlView.text = item.url + siteItemView.overflowView.showAndEnable() + siteItemView.overflowView.hideAndDisable() + siteItemView.changeSelected(any()) + holder.setColorsAndIcons(item.url) + } + } + + @Test + fun `binding an item with a null title uses the url as the title`() { + val item = item.copy(title = null) + holder.bind(item, BookmarkFragmentState.Mode.Normal()) + + verify { siteItemView.titleView.text = item.url } + } + + @Test + fun `binding an item with a blank title uses the url as the title`() { + val item = item.copy(title = " ") + holder.bind(item, BookmarkFragmentState.Mode.Normal()) + + verify { siteItemView.titleView.text = item.url } + } + + @Test + fun `rebinds title if item title is null and the item url has changed`() { + val item = item.copy(title = null) + holder.bind( + item, + BookmarkFragmentState.Mode.Normal(), + BookmarkPayload( + titleChanged = false, + urlChanged = true, + selectedChanged = false, + modeChanged = false + ) + ) + + verify { siteItemView.titleView.text = item.url } + } + + @Test + fun `rebinds title if item title is blank and the item url has changed`() { + val item = item.copy(title = " ") + holder.bind( + item, + BookmarkFragmentState.Mode.Normal(), + BookmarkPayload( + titleChanged = false, + urlChanged = true, + selectedChanged = false, + modeChanged = false + ) + ) + + verify { siteItemView.titleView.text = item.url } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/library/history/HistoryItemMenuTest.kt b/app/src/test/java/org/mozilla/fenix/library/history/HistoryItemMenuTest.kt new file mode 100644 index 0000000000..356c464d86 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/library/history/HistoryItemMenuTest.kt @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.history + +import android.content.Context +import androidx.appcompat.view.ContextThemeWrapper +import io.mockk.mockk +import io.mockk.verify +import mozilla.components.concept.menu.candidate.TextStyle +import mozilla.components.support.ktx.android.content.getColorFromAttr +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.library.history.HistoryItemMenu.Item + +@RunWith(FenixRobolectricTestRunner::class) +class HistoryItemMenuTest { + + private lateinit var context: Context + private lateinit var onItemTapped: (Item) -> Unit + private lateinit var menu: HistoryItemMenu + + @Before + fun setup() { + context = ContextThemeWrapper(testContext, R.style.NormalTheme) + onItemTapped = mockk(relaxed = true) + menu = HistoryItemMenu(context, onItemTapped) + } + + @Test + fun `delete item has special styling`() { + val deleteItem = menu.menuItems().last() + assertEquals("Delete", deleteItem.text) + assertEquals( + TextStyle(color = context.getColorFromAttr(R.attr.destructive)), + deleteItem.textStyle + ) + + deleteItem.onClick() + verify { onItemTapped(Item.Delete) } + } + + @Test + fun `builds menu items`() { + val items = menu.menuItems() + assertEquals(5, items.size) + val (copy, share, openInNewTab, openInPrivateTab, delete) = items + + assertEquals("Copy", copy.text) + assertEquals("Share", share.text) + assertEquals("Open in new tab", openInNewTab.text) + assertEquals("Open in private tab", openInPrivateTab.text) + assertEquals("Delete", delete.text) + + copy.onClick() + verify { onItemTapped(Item.Copy) } + + share.onClick() + verify { onItemTapped(Item.Share) } + + openInNewTab.onClick() + verify { onItemTapped(Item.OpenInNewTab) } + + openInPrivateTab.onClick() + verify { onItemTapped(Item.OpenInPrivateTab) } + + delete.onClick() + verify { onItemTapped(Item.Delete) } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/search/SearchFragmentStoreTest.kt b/app/src/test/java/org/mozilla/fenix/search/SearchFragmentStoreTest.kt index 00b50a0a0d..39c65111df 100644 --- a/app/src/test/java/org/mozilla/fenix/search/SearchFragmentStoreTest.kt +++ b/app/src/test/java/org/mozilla/fenix/search/SearchFragmentStoreTest.kt @@ -4,20 +4,144 @@ package org.mozilla.fenix.search +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import mozilla.components.browser.search.SearchEngine +import mozilla.components.browser.search.provider.SearchEngineList +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.ContentState +import mozilla.components.browser.state.state.TabSessionState import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNotSame import org.junit.Assert.assertTrue +import org.junit.Before import org.junit.Test -import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.browser.browsingmode.BrowsingMode +import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager +import org.mozilla.fenix.components.Components +import org.mozilla.fenix.components.metrics.Event.PerformedSearch.SearchAccessPoint +import org.mozilla.fenix.components.searchengine.FenixSearchEngineProvider +import org.mozilla.fenix.utils.Settings @ExperimentalCoroutinesApi class SearchFragmentStoreTest { + @MockK private lateinit var searchEngine: SearchEngine + @MockK private lateinit var searchProvider: FenixSearchEngineProvider + @MockK private lateinit var activity: HomeActivity + @MockK(relaxed = true) private lateinit var components: Components + @MockK(relaxed = true) private lateinit var settings: Settings + + @Before + fun setup() { + MockKAnnotations.init(this) + every { activity.browsingModeManager } returns object : BrowsingModeManager { + override var mode: BrowsingMode = BrowsingMode.Normal + } + every { components.settings } returns settings + every { components.search.provider } returns searchProvider + every { searchProvider.getDefaultEngine(activity) } returns searchEngine + every { searchProvider.installedSearchEngines(activity) } returns SearchEngineList( + list = listOf(mockk(), mockk()), + default = searchEngine + ) + } + + @Test + fun `createInitialSearchFragmentState with no tab`() { + activity.browsingModeManager.mode = BrowsingMode.Normal + every { components.core.store.state } returns BrowserState() + every { settings.shouldShowSearchShortcuts } returns true + + val expected = SearchFragmentState( + query = "", + url = "", + searchTerms = "", + searchEngineSource = SearchEngineSource.Default(searchEngine), + defaultEngineSource = SearchEngineSource.Default(searchEngine), + showSearchSuggestions = false, + showSearchSuggestionsHint = false, + showSearchShortcuts = true, + areShortcutsAvailable = true, + showClipboardSuggestions = false, + showHistorySuggestions = false, + showBookmarkSuggestions = false, + tabId = null, + pastedText = "pastedText", + searchAccessPoint = SearchAccessPoint.ACTION + ) + + assertEquals( + expected, + createInitialSearchFragmentState( + activity, + components, + tabId = null, + pastedText = "pastedText", + searchAccessPoint = SearchAccessPoint.ACTION + ) + ) + assertEquals( + expected.copy(tabId = "tabId"), + createInitialSearchFragmentState( + activity, + components, + tabId = "tabId", + pastedText = "pastedText", + searchAccessPoint = SearchAccessPoint.ACTION + ) + ) + } + + @Test + fun `createInitialSearchFragmentState with tab`() { + activity.browsingModeManager.mode = BrowsingMode.Private + every { components.core.store.state } returns BrowserState( + tabs = listOf( + TabSessionState( + id = "tabId", + content = ContentState( + url = "https://example.com", + searchTerms = "search terms" + ) + ) + ) + ) + + assertEquals( + SearchFragmentState( + query = "https://example.com", + url = "https://example.com", + searchTerms = "search terms", + searchEngineSource = SearchEngineSource.Default(searchEngine), + defaultEngineSource = SearchEngineSource.Default(searchEngine), + showSearchSuggestions = false, + showSearchSuggestionsHint = false, + showSearchShortcuts = false, + areShortcutsAvailable = true, + showClipboardSuggestions = false, + showHistorySuggestions = false, + showBookmarkSuggestions = false, + tabId = "tabId", + pastedText = "", + searchAccessPoint = SearchAccessPoint.SHORTCUT + ), + createInitialSearchFragmentState( + activity, + components, + tabId = "tabId", + pastedText = "", + searchAccessPoint = SearchAccessPoint.SHORTCUT + ) + ) + } + @Test fun updateQuery() = runBlocking { val initialState = emptyDefaultState() @@ -33,7 +157,6 @@ class SearchFragmentStoreTest { fun selectSearchShortcutEngine() = runBlocking { val initialState = emptyDefaultState() val store = SearchFragmentStore(initialState) - val searchEngine: SearchEngine = mockk() store.dispatch(SearchFragmentAction.SearchShortcutEngineSelected(searchEngine)).join() assertNotSame(initialState, store.state) @@ -91,11 +214,10 @@ class SearchFragmentStoreTest { fun selectNewDefaultEngine() = runBlocking { val initialState = emptyDefaultState() val store = SearchFragmentStore(initialState) - val engine = mockk() - store.dispatch(SearchFragmentAction.SelectNewDefaultSearchEngine(engine)).join() + store.dispatch(SearchFragmentAction.SelectNewDefaultSearchEngine(searchEngine)).join() assertNotSame(initialState, store.state) - assertEquals(SearchEngineSource.Default(engine), store.state.searchEngineSource) + assertEquals(SearchEngineSource.Default(searchEngine), store.state.searchEngineSource) } private fun emptyDefaultState(): SearchFragmentState = SearchFragmentState( @@ -112,6 +234,6 @@ class SearchFragmentStoreTest { showClipboardSuggestions = false, showHistorySuggestions = false, showBookmarkSuggestions = false, - searchAccessPoint = Event.PerformedSearch.SearchAccessPoint.NONE + searchAccessPoint = SearchAccessPoint.NONE ) } diff --git a/app/src/test/java/org/mozilla/fenix/searchdialog/SearchDialogControllerTest.kt b/app/src/test/java/org/mozilla/fenix/searchdialog/SearchDialogControllerTest.kt new file mode 100644 index 0000000000..cfa3649c67 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/searchdialog/SearchDialogControllerTest.kt @@ -0,0 +1,320 @@ +/* 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.searchdialog + +import androidx.navigation.NavController +import androidx.navigation.NavDirections +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.unmockkObject +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runBlockingTest +import mozilla.components.browser.search.SearchEngine +import mozilla.components.browser.session.Session +import mozilla.components.browser.session.SessionManager +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.mozilla.fenix.BrowserDirection +import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.R +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MetricController +import org.mozilla.fenix.components.metrics.MetricsUtils +import org.mozilla.fenix.ext.navigateSafe +import org.mozilla.fenix.search.SearchFragmentAction +import org.mozilla.fenix.settings.SupportUtils +import org.mozilla.fenix.utils.Settings + +@ExperimentalCoroutinesApi +class SearchDialogControllerTest { + + @MockK(relaxed = true) private lateinit var activity: HomeActivity + @MockK(relaxed = true) private lateinit var store: SearchDialogFragmentStore + @MockK(relaxed = true) private lateinit var navController: NavController + @MockK private lateinit var searchEngine: SearchEngine + @MockK(relaxed = true) private lateinit var metrics: MetricController + @MockK(relaxed = true) private lateinit var settings: Settings + @MockK private lateinit var sessionManager: SessionManager + @MockK(relaxed = true) private lateinit var clearToolbarFocus: () -> Unit + + private lateinit var controller: SearchDialogController + + @Before + fun setUp() { + MockKAnnotations.init(this) + mockkObject(MetricsUtils) + + every { store.state.tabId } returns "test-tab-id" + every { store.state.searchEngineSource.searchEngine } returns searchEngine + every { sessionManager.select(any()) } just Runs + every { MetricsUtils.createSearchEvent(searchEngine, activity, any()) } returns null + + controller = SearchDialogController( + activity = activity, + sessionManager = sessionManager, + store = store, + navController = navController, + settings = settings, + metrics = metrics, + clearToolbarFocus = clearToolbarFocus + ) + } + + @After + fun teardown() { + unmockkObject(MetricsUtils) + } + + @Test + fun handleUrlCommitted() { + val url = "https://www.google.com/" + + controller.handleUrlCommitted(url) + + verify { + activity.openToBrowserAndLoad( + searchTermOrURL = url, + newTab = false, + from = BrowserDirection.FromSearchDialog, + engine = searchEngine + ) + } + verify { metrics.track(Event.EnteredUrl(false)) } + } + + @Test + fun handleSearchCommitted() { + val searchTerm = "Firefox" + + controller.handleUrlCommitted(searchTerm) + + verify { + activity.openToBrowserAndLoad( + searchTermOrURL = searchTerm, + newTab = false, + from = BrowserDirection.FromSearchDialog, + engine = searchEngine + ) + } + verify { settings.incrementActiveSearchCount() } + } + + @Test + fun handleCrashesUrlCommitted() { + val url = "about:crashes" + every { activity.packageName } returns "org.mozilla.fenix" + + controller.handleUrlCommitted(url) + + verify { + activity.startActivity(any()) + } + } + + @Test + fun handleMozillaUrlCommitted() { + val url = "moz://a" + + controller.handleUrlCommitted(url) + + verify { + activity.openToBrowserAndLoad( + searchTermOrURL = SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.MANIFESTO), + newTab = false, + from = BrowserDirection.FromSearchDialog, + engine = searchEngine + ) + } + verify { metrics.track(Event.EnteredUrl(false)) } + } + + @Test + fun handleEditingCancelled() = runBlockingTest { + controller.handleEditingCancelled() + + verify { + clearToolbarFocus() + } + } + + @Test + fun handleTextChangedNonEmpty() { + val text = "fenix" + + controller.handleTextChanged(text) + + verify { store.dispatch(SearchFragmentAction.UpdateQuery(text)) } + } + + @Test + fun handleTextChangedEmpty() { + val text = "" + + controller.handleTextChanged(text) + + verify { store.dispatch(SearchFragmentAction.UpdateQuery(text)) } + } + + @Test + fun `show search shortcuts when setting enabled AND query empty`() { + val text = "" + every { settings.shouldShowSearchShortcuts } returns true + + controller.handleTextChanged(text) + + verify { store.dispatch(SearchFragmentAction.ShowSearchShortcutEnginePicker(true)) } + } + + @Test + fun `show search shortcuts when setting enabled AND query equals url`() { + val text = "mozilla.org" + every { store.state.url } returns "mozilla.org" + every { settings.shouldShowSearchShortcuts } returns true + + controller.handleTextChanged(text) + + verify { store.dispatch(SearchFragmentAction.ShowSearchShortcutEnginePicker(true)) } + } + + @Test + fun `do not show search shortcuts when setting enabled AND query non-empty`() { + val text = "mozilla" + + controller.handleTextChanged(text) + + verify { store.dispatch(SearchFragmentAction.ShowSearchShortcutEnginePicker(false)) } + } + + @Test + fun `do not show search shortcuts when setting disabled AND query empty AND url not matching query`() { + every { settings.shouldShowSearchShortcuts } returns false + + val text = "" + + controller.handleTextChanged(text) + + verify { store.dispatch(SearchFragmentAction.ShowSearchShortcutEnginePicker(false)) } + } + + @Test + fun `do not show search shortcuts when setting disabled AND query non-empty`() { + every { settings.shouldShowSearchShortcuts } returns false + + val text = "mozilla" + + controller.handleTextChanged(text) + + verify { store.dispatch(SearchFragmentAction.ShowSearchShortcutEnginePicker(false)) } + } + + @Test + fun handleUrlTapped() { + val url = "https://www.google.com/" + + controller.handleUrlTapped(url) + + verify { + activity.openToBrowserAndLoad( + searchTermOrURL = url, + newTab = false, + from = BrowserDirection.FromSearchDialog + ) + } + verify { metrics.track(Event.EnteredUrl(false)) } + } + + @Test + fun handleSearchTermsTapped() { + val searchTerms = "fenix" + + controller.handleSearchTermsTapped(searchTerms) + + verify { + activity.openToBrowserAndLoad( + searchTermOrURL = searchTerms, + newTab = false, + from = BrowserDirection.FromSearchDialog, + engine = searchEngine, + forceSearch = true + ) + } + } + + @Test + fun handleSearchShortcutEngineSelected() { + val searchEngine: SearchEngine = mockk(relaxed = true) + + controller.handleSearchShortcutEngineSelected(searchEngine) + + verify { store.dispatch(SearchFragmentAction.SearchShortcutEngineSelected(searchEngine)) } + verify { metrics.track(Event.SearchShortcutSelected(searchEngine, false)) } + } + + @Test + fun handleClickSearchEngineSettings() { + val directions: NavDirections = + SearchDialogFragmentDirections.actionGlobalSearchEngineFragment() + + controller.handleClickSearchEngineSettings() + + verify { navController.navigateSafe(R.id.searchEngineFragment, directions) } + } + + @Test + fun handleSearchShortcutsButtonClicked_alreadyOpen() { + every { store.state.showSearchShortcuts } returns true + + controller.handleSearchShortcutsButtonClicked() + + verify { store.dispatch(SearchFragmentAction.ShowSearchShortcutEnginePicker(false)) } + } + + @Test + fun handleSearchShortcutsButtonClicked_notYetOpen() { + every { store.state.showSearchShortcuts } returns false + + controller.handleSearchShortcutsButtonClicked() + + verify { store.dispatch(SearchFragmentAction.ShowSearchShortcutEnginePicker(true)) } + } + + @Test + fun handleExistingSessionSelected() { + val session = mockk() + + controller.handleExistingSessionSelected(session) + + verify { sessionManager.select(session) } + verify { activity.openToBrowser(from = BrowserDirection.FromSearchDialog) } + } + + @Test + fun handleExistingSessionSelected_tabId_nullSession() { + every { sessionManager.findSessionById("tab-id") } returns null + + controller.handleExistingSessionSelected("tab-id") + + verify(inverse = true) { sessionManager.select(any()) } + verify(inverse = true) { activity.openToBrowser(from = BrowserDirection.FromSearchDialog) } + } + + @Test + fun handleExistingSessionSelected_tabId() { + val session = mockk() + every { sessionManager.findSessionById("tab-id") } returns session + + controller.handleExistingSessionSelected("tab-id") + + verify { sessionManager.select(any()) } + verify { activity.openToBrowser(from = BrowserDirection.FromSearchDialog) } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/session/NotificationSessionObserverTest.kt b/app/src/test/java/org/mozilla/fenix/session/NotificationSessionObserverTest.kt deleted file mode 100644 index 87d462ecf2..0000000000 --- a/app/src/test/java/org/mozilla/fenix/session/NotificationSessionObserverTest.kt +++ /dev/null @@ -1,87 +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.session - -import android.content.Context -import io.mockk.Called -import io.mockk.MockKAnnotations -import io.mockk.confirmVerified -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking -import mozilla.components.browser.state.action.CustomTabListAction -import mozilla.components.browser.state.action.TabListAction -import mozilla.components.browser.state.state.createCustomTab -import mozilla.components.browser.state.state.createTab -import mozilla.components.browser.state.store.BrowserStore -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mozilla.fenix.ext.components -import org.mozilla.fenix.helpers.FenixRobolectricTestRunner - -@ExperimentalCoroutinesApi -@RunWith(FenixRobolectricTestRunner::class) -class NotificationSessionObserverTest { - - private lateinit var observer: NotificationSessionObserver - private lateinit var store: BrowserStore - @MockK private lateinit var context: Context - @MockK(relaxed = true) private lateinit var notificationService: SessionNotificationService.Companion - - @Before - fun before() { - MockKAnnotations.init(this) - store = BrowserStore() - every { context.components.core.store } returns store - observer = NotificationSessionObserver(context, notificationService) - NotificationSessionObserver.isStartedFromPrivateShortcut = false - } - - @Test - fun `GIVEN session is private and non-custom WHEN it is added THEN notification service should be started`() = runBlocking { - val privateSession = createTab("https://firefox.com", private = true) - - store.dispatch(TabListAction.AddTabAction(privateSession)).join() - - observer.start() - verify(exactly = 1) { notificationService.start(context, false) } - confirmVerified(notificationService) - } - - @Test - fun `GIVEN session is not private WHEN it is added THEN notification service should not be started`() = runBlocking { - val normalSession = createTab("https://firefox.com") - val customSession = createCustomTab("https://firefox.com") - - observer.start() - verify { notificationService wasNot Called } - - store.dispatch(TabListAction.AddTabAction(normalSession)).join() - verify(exactly = 0) { notificationService.start(context, false) } - - store.dispatch(CustomTabListAction.AddCustomTabAction(customSession)).join() - verify(exactly = 0) { notificationService.start(context, false) } - } - - @Test - fun `GIVEN session is custom tab WHEN it is added THEN notification service should not be started`() = runBlocking { - val privateCustomSession = createCustomTab("https://firefox.com").let { - it.copy(content = it.content.copy(private = true)) - } - val customSession = createCustomTab("https://firefox.com") - - observer.start() - verify { notificationService wasNot Called } - - store.dispatch(CustomTabListAction.AddCustomTabAction(privateCustomSession)).join() - verify(exactly = 0) { notificationService.start(context, false) } - - store.dispatch(CustomTabListAction.AddCustomTabAction(customSession)).join() - verify(exactly = 0) { notificationService.start(context, false) } - } -} diff --git a/app/src/test/java/org/mozilla/fenix/session/PrivateNotificationServiceTest.kt b/app/src/test/java/org/mozilla/fenix/session/PrivateNotificationServiceTest.kt new file mode 100644 index 0000000000..d0267c53d4 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/session/PrivateNotificationServiceTest.kt @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.session + +import android.content.ComponentName +import android.content.Intent +import io.mockk.every +import io.mockk.mockk +import mozilla.components.feature.privatemode.notification.AbstractPrivateNotificationService.Companion.ACTION_ERASE +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.HomeActivity +import org.mozilla.fenix.HomeActivity.Companion.PRIVATE_BROWSING_MODE +import org.mozilla.fenix.ext.components +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.robolectric.Robolectric +import org.robolectric.Shadows.shadowOf +import org.robolectric.android.controller.ServiceController + +@RunWith(FenixRobolectricTestRunner::class) +class PrivateNotificationServiceTest { + + private lateinit var controller: ServiceController + + @Before + fun setup() { + val store = testContext.components.core.store + every { store.dispatch(any()) } returns mockk() + + controller = Robolectric.buildService( + PrivateNotificationService::class.java, + Intent(ACTION_ERASE) + ) + } + + @Test + fun `service opens home activity with PBM flag set to true`() { + PrivateNotificationService.isStartedFromPrivateShortcut = true + val service = shadowOf(controller.get()) + controller.startCommand(0, 0) + + val intent = service.nextStartedActivity + assertEquals(ComponentName(testContext, HomeActivity::class.java), intent.component) + assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK, intent.flags) + assertEquals(true, intent.extras?.getBoolean(PRIVATE_BROWSING_MODE)) + } + + @Test + fun `service opens home activity with PBM flag set to false`() { + PrivateNotificationService.isStartedFromPrivateShortcut = false + val service = shadowOf(controller.get()) + controller.startCommand(0, 0) + + val intent = service.nextStartedActivity + assertEquals(ComponentName(testContext, HomeActivity::class.java), intent.component) + assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK, intent.flags) + assertEquals(false, intent.extras?.getBoolean(PRIVATE_BROWSING_MODE)) + } +} diff --git a/app/src/test/java/org/mozilla/fenix/session/SessionNotificationServiceTest.kt b/app/src/test/java/org/mozilla/fenix/session/SessionNotificationServiceTest.kt deleted file mode 100644 index 2a9e5bccd1..0000000000 --- a/app/src/test/java/org/mozilla/fenix/session/SessionNotificationServiceTest.kt +++ /dev/null @@ -1,27 +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.session - -import mozilla.components.support.test.robolectric.testContext -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test -import org.junit.runner.RunWith -import org.mozilla.fenix.helpers.FenixRobolectricTestRunner - -@RunWith(FenixRobolectricTestRunner::class) -class SessionNotificationServiceTest { - - @Test - fun `Service keeps tracked of started state`() { - assertFalse(SessionNotificationService.started) - - SessionNotificationService.start(testContext, false) - assertTrue(SessionNotificationService.started) - - SessionNotificationService.stop(testContext) - assertFalse(SessionNotificationService.started) - } -} diff --git a/app/src/test/java/org/mozilla/fenix/settings/about/AboutLibrariesActivityTest.kt b/app/src/test/java/org/mozilla/fenix/settings/about/AboutLibrariesActivityTest.kt new file mode 100644 index 0000000000..140dc44aeb --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/about/AboutLibrariesActivityTest.kt @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.settings.about + +import android.widget.ListView +import android.widget.TextView +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.robolectric.Robolectric +import org.robolectric.Shadows.shadowOf +import org.robolectric.shadows.ShadowAlertDialog + +@RunWith(FenixRobolectricTestRunner::class) +class AboutLibrariesActivityTest { + @Test + fun `activity should display licenses`() { + val activity = Robolectric.buildActivity(AboutLibrariesActivity::class.java).create().get() + val listView = activity.findViewById(R.id.about_libraries_listview) + + assertTrue(0 < listView.count) + } + + @Test + fun `item click should open license dialog`() { + val activity = Robolectric.buildActivity(AboutLibrariesActivity::class.java).create().get() + + val listView = activity.findViewById(R.id.about_libraries_listview) + val listViewShadow = shadowOf(listView) + listViewShadow.clickFirstItemContainingText("org.mozilla.geckoview:geckoview") + + val alertDialogShadow = ShadowAlertDialog.getLatestDialog() + assertTrue(alertDialogShadow.isShowing) + + val alertDialogText = alertDialogShadow + .findViewById(android.R.id.message) + .text + .toString() + assertTrue(alertDialogText.contains("MPL")) + } +} diff --git a/build.gradle b/build.gradle index a236dc1911..3ea76aa37a 100644 --- a/build.gradle +++ b/build.gradle @@ -198,5 +198,5 @@ tasks.register("githubLintDetektDetails", GithubDetailsTask) { } tasks.register("githubLintAndroidDetails", GithubDetailsTask) { - text = "### [Android Lint Results]({reportsUrl}/lint-results-geckoNightlyDebug.html)" + text = "### [Android Lint Results]({reportsUrl}/lint-results-debug.html)" } diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 7ceac88cb7..c429e7980e 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "54.0.20200812130143" + const val VERSION = "54.0.20200814130102" } diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index 876e537dd3..40933b3c02 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -10,7 +10,6 @@ object Versions { const val leakcanary = "2.4" const val leanplum = "5.4.0" const val osslicenses_plugin = "0.9.5" - const val osslicenses_library = "17.0.0" const val detekt = "1.9.1" const val androidx_appcompat = "1.2.0-rc01" @@ -59,7 +58,6 @@ object Deps { const val allopen = "org.jetbrains.kotlin:kotlin-allopen:${Versions.kotlin}" const val osslicenses_plugin = "com.google.android.gms:oss-licenses-plugin:${Versions.osslicenses_plugin}" - const val osslicenses_library = "com.google.android.gms:play-services-oss-licenses:${Versions.osslicenses_library}" const val mozilla_concept_engine = "org.mozilla.components:concept-engine:${Versions.mozilla_android_components}" const val mozilla_concept_menu = "org.mozilla.components:concept-menu:${Versions.mozilla_android_components}"