From e475ba0aa50422fccf75fc3d1e2734ca66340cf9 Mon Sep 17 00:00:00 2001 From: Marcos Holgado Date: Wed, 19 May 2021 08:05:29 +0100 Subject: [PATCH 1/4] Delete uoa experiment --- .../34.json | 866 ++++++++++++++++++ .../app/browser/BrowserTabViewModelTest.kt | 243 +---- .../app/browser/BrowserViewModelTest.kt | 13 +- .../logindetection/JsLoginDetectorTest.kt | 6 +- .../browser/shortcut/ShortcutReceiverTest.kt | 77 +- .../java/com/duckduckgo/app/cta/ui/CtaTest.kt | 36 - .../duckduckgo/app/cta/ui/CtaViewModelTest.kt | 97 -- .../app/global/db/AppDatabaseTest.kt | 10 +- .../app/global/events/db/UserEventsDaoTest.kt | 14 +- .../global/useourapp/UseOurAppDetectorTest.kt | 160 ---- .../AndroidNotificationSchedulerTest.kt | 201 +--- .../NotificationHandlerServiceTest.kt | 24 - .../model/UseOurAppNotificationTest.kt | 76 -- .../onboarding/store/AppUserStageStoreTest.kt | 82 +- .../app/statistics/VariantManagerTest.kt | 44 - .../systemsearch/SystemSearchViewModelTest.kt | 2 +- .../app/tabs/model/TabDataRepositoryTest.kt | 37 +- .../app/browser/BrowserTabViewModel.kt | 48 +- .../app/browser/BrowserViewModel.kt | 15 +- .../app/browser/di/BrowserModule.kt | 5 +- .../logindetection/DOMLoginDetector.kt | 5 +- .../app/browser/shortcut/ShortcutBuilder.kt | 12 +- .../app/browser/shortcut/ShortcutReceiver.kt | 22 +- .../duckduckgo/app/cta/model/DismissedCta.kt | 2 - .../java/com/duckduckgo/app/cta/ui/Cta.kt | 50 - .../com/duckduckgo/app/cta/ui/CtaViewModel.kt | 42 - .../com/duckduckgo/app/di/DatabaseModule.kt | 10 +- .../duckduckgo/app/di/NotificationModule.kt | 24 +- .../com/duckduckgo/app/di/VariantModule.kt | 6 - .../app/global/DuckDuckGoApplication.kt | 2 +- .../duckduckgo/app/global/db/AppDatabase.kt | 22 +- .../app/global/events/db/UserEventEntity.kt | 2 - .../app/global/useourapp/UseOurAppDetector.kt | 74 -- .../useourapp/UseOurAppMigrationManager.kt | 45 - .../java/com/duckduckgo/app/job/JobCleaner.kt | 3 +- .../AndroidNotificationScheduler.kt | 85 +- .../NotificationHandlerService.kt | 19 - .../app/notification/NotificationRegistrar.kt | 1 - .../model/UseOurAppNotification.kt | 73 -- .../app/onboarding/store/UserStage.kt | 2 - .../app/onboarding/store/UserStageStore.kt | 36 +- .../com/duckduckgo/app/pixels/AppPixelName.kt | 11 - .../app/tabs/model/TabDataRepository.kt | 9 +- .../main/res/values/string-untranslated.xml | 14 +- .../app/statistics/VariantManager.kt | 12 +- 45 files changed, 937 insertions(+), 1702 deletions(-) create mode 100644 app/schemas/com.duckduckgo.app.global.db.AppDatabase/34.json delete mode 100644 app/src/androidTest/java/com/duckduckgo/app/global/useourapp/UseOurAppDetectorTest.kt delete mode 100644 app/src/androidTest/java/com/duckduckgo/app/notification/model/UseOurAppNotificationTest.kt delete mode 100644 app/src/main/java/com/duckduckgo/app/global/useourapp/UseOurAppDetector.kt delete mode 100644 app/src/main/java/com/duckduckgo/app/global/useourapp/UseOurAppMigrationManager.kt delete mode 100644 app/src/main/java/com/duckduckgo/app/notification/model/UseOurAppNotification.kt diff --git a/app/schemas/com.duckduckgo.app.global.db.AppDatabase/34.json b/app/schemas/com.duckduckgo.app.global.db.AppDatabase/34.json new file mode 100644 index 000000000000..eef10f84bcf5 --- /dev/null +++ b/app/schemas/com.duckduckgo.app.global.db.AppDatabase/34.json @@ -0,0 +1,866 @@ +{ + "formatVersion": 1, + "database": { + "version": 34, + "identityHash": "be6380785dcbf8f6793f852097f0d224", + "entities": [ + { + "tableName": "tds_tracker", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`domain` TEXT NOT NULL, `defaultAction` TEXT NOT NULL, `ownerName` TEXT NOT NULL, `categories` TEXT NOT NULL, `rules` TEXT NOT NULL, PRIMARY KEY(`domain`))", + "fields": [ + { + "fieldPath": "domain", + "columnName": "domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "defaultAction", + "columnName": "defaultAction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ownerName", + "columnName": "ownerName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "categories", + "columnName": "categories", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rules", + "columnName": "rules", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "domain" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "tds_entity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `displayName` TEXT NOT NULL, `prevalence` REAL NOT NULL, PRIMARY KEY(`name`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "prevalence", + "columnName": "prevalence", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "name" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "tds_domain_entity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`domain` TEXT NOT NULL, `entityName` TEXT NOT NULL, PRIMARY KEY(`domain`))", + "fields": [ + { + "fieldPath": "domain", + "columnName": "domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entityName", + "columnName": "entityName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "domain" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "temporary_tracking_whitelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`domain` TEXT NOT NULL, PRIMARY KEY(`domain`))", + "fields": [ + { + "fieldPath": "domain", + "columnName": "domain", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "domain" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "user_whitelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`domain` TEXT NOT NULL, PRIMARY KEY(`domain`))", + "fields": [ + { + "fieldPath": "domain", + "columnName": "domain", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "domain" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "https_bloom_filter_spec", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `bitCount` INTEGER NOT NULL, `errorRate` REAL NOT NULL, `totalEntries` INTEGER NOT NULL, `sha256` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "bitCount", + "columnName": "bitCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorRate", + "columnName": "errorRate", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "totalEntries", + "columnName": "totalEntries", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sha256", + "columnName": "sha256", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "https_false_positive_domain", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`domain` TEXT NOT NULL, PRIMARY KEY(`domain`))", + "fields": [ + { + "fieldPath": "domain", + "columnName": "domain", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "domain" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "network_leaderboard", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`networkName` TEXT NOT NULL, `count` INTEGER NOT NULL, PRIMARY KEY(`networkName`))", + "fields": [ + { + "fieldPath": "networkName", + "columnName": "networkName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "count", + "columnName": "count", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "networkName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "sites_visited", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `count` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "count", + "columnName": "count", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "tabs", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tabId` TEXT NOT NULL, `url` TEXT, `title` TEXT, `skipHome` INTEGER NOT NULL, `viewed` INTEGER NOT NULL, `position` INTEGER NOT NULL, `tabPreviewFile` TEXT, `sourceTabId` TEXT, `deletable` INTEGER NOT NULL, PRIMARY KEY(`tabId`), FOREIGN KEY(`sourceTabId`) REFERENCES `tabs`(`tabId`) ON UPDATE SET NULL ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "tabId", + "columnName": "tabId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "skipHome", + "columnName": "skipHome", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "viewed", + "columnName": "viewed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tabPreviewFile", + "columnName": "tabPreviewFile", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sourceTabId", + "columnName": "sourceTabId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "deletable", + "columnName": "deletable", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "tabId" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_tabs_tabId", + "unique": false, + "columnNames": [ + "tabId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_tabs_tabId` ON `${TABLE_NAME}` (`tabId`)" + } + ], + "foreignKeys": [ + { + "table": "tabs", + "onDelete": "SET NULL", + "onUpdate": "SET NULL", + "columns": [ + "sourceTabId" + ], + "referencedColumns": [ + "tabId" + ] + } + ] + }, + { + "tableName": "tab_selection", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `tabId` TEXT, PRIMARY KEY(`id`), FOREIGN KEY(`tabId`) REFERENCES `tabs`(`tabId`) ON UPDATE NO ACTION ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tabId", + "columnName": "tabId", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_tab_selection_tabId", + "unique": false, + "columnNames": [ + "tabId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_tab_selection_tabId` ON `${TABLE_NAME}` (`tabId`)" + } + ], + "foreignKeys": [ + { + "table": "tabs", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "tabId" + ], + "referencedColumns": [ + "tabId" + ] + } + ] + }, + { + "tableName": "bookmarks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `url` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "survey", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`surveyId` TEXT NOT NULL, `url` TEXT, `daysInstalled` INTEGER, `status` TEXT NOT NULL, PRIMARY KEY(`surveyId`))", + "fields": [ + { + "fieldPath": "surveyId", + "columnName": "surveyId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "daysInstalled", + "columnName": "daysInstalled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "surveyId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "dismissed_cta", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`ctaId` TEXT NOT NULL, PRIMARY KEY(`ctaId`))", + "fields": [ + { + "fieldPath": "ctaId", + "columnName": "ctaId", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "ctaId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "search_count", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `count` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "count", + "columnName": "count", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "app_days_used", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`date` TEXT NOT NULL, PRIMARY KEY(`date`))", + "fields": [ + { + "fieldPath": "date", + "columnName": "date", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "date" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "app_enjoyment", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`eventType` INTEGER NOT NULL, `promptCount` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `primaryKey` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "eventType", + "columnName": "eventType", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "promptCount", + "columnName": "promptCount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "primaryKey", + "columnName": "primaryKey", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "primaryKey" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "notification", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`notificationId` TEXT NOT NULL, PRIMARY KEY(`notificationId`))", + "fields": [ + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "notificationId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "privacy_protection_count", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `blocked_tracker_count` INTEGER NOT NULL, `upgrade_count` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "blockedTrackerCount", + "columnName": "blocked_tracker_count", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "upgradeCount", + "columnName": "upgrade_count", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "UncaughtExceptionEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `exceptionSource` TEXT NOT NULL, `message` TEXT NOT NULL, `version` TEXT NOT NULL, `timestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exceptionSource", + "columnName": "exceptionSource", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "tdsMetadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `eTag` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "eTag", + "columnName": "eTag", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "userStage", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` INTEGER NOT NULL, `appStage` TEXT NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "appStage", + "columnName": "appStage", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "fireproofWebsites", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`domain` TEXT NOT NULL, PRIMARY KEY(`domain`))", + "fields": [ + { + "fieldPath": "domain", + "columnName": "domain", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "domain" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "user_events", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "locationPermissions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`domain` TEXT NOT NULL, `permission` INTEGER NOT NULL, PRIMARY KEY(`domain`))", + "fields": [ + { + "fieldPath": "domain", + "columnName": "domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "permission", + "columnName": "permission", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "domain" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "pixel_store", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `pixelName` TEXT NOT NULL, `atb` TEXT NOT NULL, `additionalQueryParams` TEXT NOT NULL, `encodedQueryParams` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pixelName", + "columnName": "pixelName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "atb", + "columnName": "atb", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "additionalQueryParams", + "columnName": "additionalQueryParams", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encodedQueryParams", + "columnName": "encodedQueryParams", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "auth_cookies_allowed_domains", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`domain` TEXT NOT NULL, PRIMARY KEY(`domain`))", + "fields": [ + { + "fieldPath": "domain", + "columnName": "domain", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "domain" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'be6380785dcbf8f6793f852097f0d224')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt index e2e70abe3d69..56ae522a9269 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt @@ -64,21 +64,15 @@ import com.duckduckgo.app.cta.ui.CtaViewModel import com.duckduckgo.app.cta.ui.DaxBubbleCta import com.duckduckgo.app.cta.ui.DaxDialogCta import com.duckduckgo.app.cta.ui.HomePanelCta -import com.duckduckgo.app.cta.ui.UseOurAppCta import com.duckduckgo.app.email.EmailManager import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteDao import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteEntity import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteRepository import com.duckduckgo.app.global.db.AppDatabase -import com.duckduckgo.app.global.events.db.UserEventEntity -import com.duckduckgo.app.global.events.db.UserEventKey import com.duckduckgo.app.global.events.db.UserEventsStore import com.duckduckgo.app.global.install.AppInstallStore import com.duckduckgo.app.global.model.Site import com.duckduckgo.app.global.model.SiteFactory -import com.duckduckgo.app.global.useourapp.UseOurAppDetector -import com.duckduckgo.app.global.useourapp.UseOurAppDetector.Companion.USE_OUR_APP_DOMAIN -import com.duckduckgo.app.global.useourapp.UseOurAppDetector.Companion.USE_OUR_APP_SHORTCUT_URL import com.duckduckgo.app.globalprivacycontrol.GlobalPrivacyControlManager import com.duckduckgo.app.globalprivacycontrol.GlobalPrivacyControlManager.Companion.GPC_HEADER import com.duckduckgo.app.globalprivacycontrol.GlobalPrivacyControlManager.Companion.GPC_HEADER_VALUE @@ -88,7 +82,6 @@ import com.duckduckgo.app.location.data.LocationPermissionType import com.duckduckgo.app.location.data.LocationPermissionsDao import com.duckduckgo.app.location.data.LocationPermissionsRepository import com.duckduckgo.app.notification.db.NotificationDao -import com.duckduckgo.app.notification.model.UseOurAppNotification import com.duckduckgo.app.onboarding.store.AppStage import com.duckduckgo.app.onboarding.store.OnboardingStore import com.duckduckgo.app.onboarding.store.UserStageStore @@ -317,12 +310,9 @@ class BrowserTabViewModelTest { mockWidgetCapabilities, mockDismissedCtaDao, mockUserWhitelistDao, - mockVariantManager, mockSettingsStore, mockOnboardingStore, mockUserStageStore, - mockUserEventsStore, - UseOurAppDetector(mockUserEventsStore), mockTabRepository, coroutineRule.testDispatcherProvider ) @@ -370,7 +360,6 @@ class BrowserTabViewModelTest { navigationAwareLoginDetector = mockNavigationAwareLoginDetector, userEventsStore = mockUserEventsStore, notificationDao = mockNotificationDao, - useOurAppDetector = UseOurAppDetector(mockUserEventsStore), variantManager = mockVariantManager, fileDownloader = mockFileDownloader, globalPrivacyControl = GlobalPrivacyControlManager(mockSettingsStore), @@ -427,8 +416,7 @@ class BrowserTabViewModelTest { } @Test - fun whenViewBecomesVisibleAndHomeShowingAndUserIsNotInUseOurAppOnboardingStageThenKeyboardShown() = coroutineRule.runBlocking { - whenever(mockUserStageStore.getUserAppStage()).thenReturn(AppStage.ESTABLISHED) + fun whenViewBecomesVisibleAndHomeShowingThenKeyboardShown() = coroutineRule.runBlocking { setBrowserShowing(false) testee.onViewVisible() @@ -436,16 +424,6 @@ class BrowserTabViewModelTest { assertTrue(commandCaptor.allValues.contains(Command.ShowKeyboard)) } - @Test - fun whenViewBecomesVisibleAndHomeShowingAndUserIsInUseOurAppOnboardingStageThenKeyboardHidden() = coroutineRule.runBlocking { - whenever(mockUserStageStore.getUserAppStage()).thenReturn(AppStage.USE_OUR_APP_ONBOARDING) - setBrowserShowing(false) - - testee.onViewVisible() - verify(mockCommandObserver, atLeastOnce()).onChanged(commandCaptor.capture()) - assertTrue(commandCaptor.allValues.contains(Command.HideKeyboard)) - } - @Test fun whenViewBecomesVisibleAndHomeCtaPresentThenKeyboardHidden() = coroutineRule.runBlocking { givenExpectedCtaAddWidgetInstructions() @@ -1890,16 +1868,6 @@ class BrowserTabViewModelTest { assertCommandIssued() } - @Test - fun whenUserClickedUseOurAppCtaOkButtonThenLaunchAddHomeShortcutAndNavigateCommand() { - whenever(mockOmnibarConverter.convertQueryToUrl(USE_OUR_APP_SHORTCUT_URL, null)).thenReturn(USE_OUR_APP_SHORTCUT_URL) - val cta = UseOurAppCta() - setCta(cta) - testee.onUserClickCtaOkButton() - assertCommandIssued() - assertCommandIssued() - } - @Test fun whenSurveyCtaDismissedAndNoOtherCtaPossibleCtaIsNull() = coroutineRule.runBlocking { givenShownCtas(CtaId.DAX_INTRO, CtaId.DAX_END) @@ -1977,14 +1945,6 @@ class BrowserTabViewModelTest { verify(mockSurveyDao).cancelScheduledSurveys() } - @Test - fun whenUserClickedSecondaryCtaButtonInUseOurAppCtaThenLaunchShowKeyboardCommand() { - val cta = UseOurAppCta() - setCta(cta) - testee.onUserClickCtaSecondaryButton() - assertCommandIssued() - } - @Test fun whenSurrogateDetectedThenSiteUpdated() { givenOneActiveTabSelected() @@ -2177,30 +2137,6 @@ class BrowserTabViewModelTest { } } - @Test - fun whenLoginDetectedAndUrlIsUseOurAppThenRegisterUserEvent() = coroutineRule.runBlocking { - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN)).thenReturn(null) - loginEventLiveData.value = givenLoginDetected(USE_OUR_APP_SHORTCUT_URL) - - verify(mockUserEventsStore).registerUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN) - } - - @Test - fun whenLoginDetectedAndUrlIsNotUseOurAppThenDoNotRegisterUserEvent() = coroutineRule.runBlocking { - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN)).thenReturn(null) - loginEventLiveData.value = givenLoginDetected("example.com") - - verify(mockUserEventsStore, never()).registerUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN) - } - - @Test - fun whenLoginDetectedAndDialogAlreadySeenThenDoNotRegisterUserEvent() = coroutineRule.runBlocking { - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN)).thenReturn(UserEventEntity(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN)) - loginEventLiveData.value = givenLoginDetected(USE_OUR_APP_SHORTCUT_URL) - - verify(mockUserEventsStore, never()).registerUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN) - } - @Test fun whenUserBrowsingPressesBackThenCannotAddBookmark() { setupNavigation(skipHome = false, isBrowsing = true, canGoBack = false) @@ -2333,163 +2269,6 @@ class BrowserTabViewModelTest { testee.onUserSubmittedQuery("about:blank") } - @Test - fun whenViewReadyIfDomainSameAsUseOurAppAfterNotificationSeenThenPixelSent() = coroutineRule.runBlocking { - givenUseOurAppSiteSelected() - whenever(mockNotificationDao.exists(UseOurAppNotification.ID)).thenReturn(true) - - testee.onViewReady() - - verify(mockPixel).fire(AppPixelName.UOA_VISITED_AFTER_NOTIFICATION) - } - - @Test - fun whenViewReadyIfDomainSameAsUseOurAppAfterShortcutAddedThenPixelSent() = coroutineRule.runBlocking { - givenUseOurAppSiteSelected() - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(UserEventEntity(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)) - - testee.onViewReady() - - verify(mockPixel).fire(AppPixelName.UOA_VISITED_AFTER_SHORTCUT) - } - - @Test - fun whenViewReadyIfDomainSameAsUseOurAppAfterDeleteCtaShownThenPixelSent() = coroutineRule.runBlocking { - givenUseOurAppSiteSelected() - whenever(mockDismissedCtaDao.exists(CtaId.USE_OUR_APP_DELETION)).thenReturn(true) - - testee.onViewReady() - - verify(mockPixel).fire(AppPixelName.UOA_VISITED_AFTER_DELETE_CTA) - } - - @Test - fun whenViewReadyIfDomainSameAsUseOurAppThenPixelSent() = coroutineRule.runBlocking { - givenUseOurAppSiteSelected() - - testee.onViewReady() - - verify(mockPixel).fire(AppPixelName.UOA_VISITED) - } - - @Test - fun whenViewReadyIfDomainIsNotTheSameAsUseOurAppAfterNotificationSeenThenPixelNotSent() = coroutineRule.runBlocking { - givenUseOurAppSiteIsNotSelected() - whenever(mockNotificationDao.exists(UseOurAppNotification.ID)).thenReturn(true) - - testee.onViewReady() - - verify(mockPixel, never()).fire(AppPixelName.UOA_VISITED_AFTER_NOTIFICATION) - } - - @Test - fun whenViewReadyIfDomainIsNotTheSameAsUseOurAppAfterShortcutAddedThenPixelNotSent() = coroutineRule.runBlocking { - givenUseOurAppSiteIsNotSelected() - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(UserEventEntity(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)) - - testee.onViewReady() - - verify(mockPixel, never()).fire(AppPixelName.UOA_VISITED_AFTER_SHORTCUT) - } - - @Test - fun whenViewReadyIfDomainIsNotTheSameAsUseOurAppAfterDeleteCtaShownThenPixelNotSent() = coroutineRule.runBlocking { - givenUseOurAppSiteIsNotSelected() - whenever(mockDismissedCtaDao.exists(CtaId.USE_OUR_APP_DELETION)).thenReturn(true) - - testee.onViewReady() - - verify(mockPixel, never()).fire(AppPixelName.UOA_VISITED_AFTER_DELETE_CTA) - } - - @Test - fun whenViewReadyIfDomainIsNotTheSameAsUseOurAppAThenPixelNotSent() = coroutineRule.runBlocking { - givenUseOurAppSiteIsNotSelected() - - testee.onViewReady() - - verify(mockPixel, never()).fire(AppPixelName.UOA_VISITED) - } - - @Test - fun whenPageChangedIfPreviousOneWasNotUseOurAppSiteAfterNotificationSeenThenPixelSent() = coroutineRule.runBlocking { - givenUseOurAppSiteIsNotSelected() - whenever(mockNotificationDao.exists(UseOurAppNotification.ID)).thenReturn(true) - - loadUrl(USE_OUR_APP_DOMAIN, isBrowserShowing = true) - - verify(mockPixel).fire(AppPixelName.UOA_VISITED_AFTER_NOTIFICATION) - } - - @Test - fun whenPageChangedIfPreviousOneWasNotUseOurAppSiteAfterShortcutAddedThenPixelSent() = coroutineRule.runBlocking { - givenUseOurAppSiteIsNotSelected() - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(UserEventEntity(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)) - - loadUrl(USE_OUR_APP_DOMAIN, isBrowserShowing = true) - - verify(mockPixel).fire(AppPixelName.UOA_VISITED_AFTER_SHORTCUT) - } - - @Test - fun whenPageChangedIfPreviousOneWasNotUseOurAppSiteAfterDeleteCtaShownThenPixelSent() = coroutineRule.runBlocking { - givenUseOurAppSiteIsNotSelected() - whenever(mockDismissedCtaDao.exists(CtaId.USE_OUR_APP_DELETION)).thenReturn(true) - - loadUrl(USE_OUR_APP_DOMAIN, isBrowserShowing = true) - - verify(mockPixel).fire(AppPixelName.UOA_VISITED_AFTER_DELETE_CTA) - } - - @Test - fun whenPageChangedIfPreviousOneWasNotUseOurAppSiteThenPixelSent() = coroutineRule.runBlocking { - givenUseOurAppSiteIsNotSelected() - - loadUrl(USE_OUR_APP_DOMAIN, isBrowserShowing = true) - - verify(mockPixel).fire(AppPixelName.UOA_VISITED) - } - - @Test - fun whenPageChangedIfPreviousOneWasUseOurAppSiteAfterNotificationSeenThenPixelNotSent() = coroutineRule.runBlocking { - givenUseOurAppSiteSelected() - whenever(mockNotificationDao.exists(UseOurAppNotification.ID)).thenReturn(true) - - loadUrl(USE_OUR_APP_DOMAIN, isBrowserShowing = true) - - verify(mockPixel, never()).fire(AppPixelName.UOA_VISITED_AFTER_NOTIFICATION) - } - - @Test - fun whenPageChangedIfPreviousOneWasUseOurAppSiteAfterShortcutAddedThenPixelNotSent() = coroutineRule.runBlocking { - givenUseOurAppSiteSelected() - val timestampEntity = UserEventEntity(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED) - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(timestampEntity) - - loadUrl(USE_OUR_APP_DOMAIN, isBrowserShowing = true) - - verify(mockPixel, never()).fire(AppPixelName.UOA_VISITED_AFTER_SHORTCUT) - } - - @Test - fun whenPageChangedIfPreviousOneWasUseOurAppSiteThenAfterDeleteCtaShownPixelNotSent() = coroutineRule.runBlocking { - givenUseOurAppSiteSelected() - whenever(mockDismissedCtaDao.exists(CtaId.USE_OUR_APP_DELETION)).thenReturn(true) - - loadUrl(USE_OUR_APP_DOMAIN, isBrowserShowing = true) - - verify(mockPixel, never()).fire(AppPixelName.UOA_VISITED_AFTER_DELETE_CTA) - } - - @Test - fun whenPageChangedIfPreviousOneWasUseOurAppSiteThenNotSent() = coroutineRule.runBlocking { - givenUseOurAppSiteSelected() - - loadUrl(USE_OUR_APP_DOMAIN, isBrowserShowing = true) - - verify(mockPixel, never()).fire(AppPixelName.UOA_VISITED) - } - @Test fun whenDosAttackDetectedThenErrorIsShown() { testee.dosAttackDetected() @@ -3365,26 +3144,6 @@ class BrowserTabViewModelTest { testee.loadData("TAB_ID", "https://example.com", false) } - private fun givenUseOurAppSiteSelected() { - whenever(mockOmnibarConverter.convertQueryToUrl(USE_OUR_APP_DOMAIN, null)).thenReturn(USE_OUR_APP_DOMAIN) - val site: Site = mock() - whenever(site.url).thenReturn(USE_OUR_APP_DOMAIN) - val siteLiveData = MutableLiveData() - siteLiveData.value = site - whenever(mockTabRepository.retrieveSiteData("TAB_ID")).thenReturn(siteLiveData) - testee.loadData("TAB_ID", USE_OUR_APP_DOMAIN, false) - } - - private fun givenUseOurAppSiteIsNotSelected() { - whenever(mockOmnibarConverter.convertQueryToUrl("example.com", null)).thenReturn("example.com") - val site: Site = mock() - whenever(site.url).thenReturn("example.com") - val siteLiveData = MutableLiveData() - siteLiveData.value = site - whenever(mockTabRepository.retrieveSiteData("TAB_ID")).thenReturn(siteLiveData) - testee.loadData("TAB_ID", "example.com", false) - } - private fun givenFireproofWebsiteDomain(vararg fireproofWebsitesDomain: String) { fireproofWebsitesDomain.forEach { fireproofWebsiteDao.insert(FireproofWebsiteEntity(domain = it)) diff --git a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserViewModelTest.kt index f8cfd17b7244..ce6b7efe29b5 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserViewModelTest.kt @@ -29,8 +29,6 @@ import com.duckduckgo.app.global.rating.AppEnjoymentPromptEmitter import com.duckduckgo.app.global.rating.AppEnjoymentPromptOptions import com.duckduckgo.app.global.rating.AppEnjoymentUserEventRecorder import com.duckduckgo.app.global.rating.PromptCount -import com.duckduckgo.app.global.useourapp.UseOurAppDetector -import com.duckduckgo.app.global.useourapp.UseOurAppDetector.Companion.USE_OUR_APP_DOMAIN import com.duckduckgo.app.pixels.AppPixelName import com.duckduckgo.app.privacy.ui.PrivacyDashboardActivity import com.duckduckgo.app.runBlocking @@ -102,7 +100,6 @@ class BrowserViewModelTest { dataClearer = mockAutomaticDataClearer, appEnjoymentPromptEmitter = mockAppEnjoymentPromptEmitter, appEnjoymentUserEventRecorder = mockAppEnjoymentUserEventRecorder, - useOurAppDetector = UseOurAppDetector(mockUserEventsStore), dispatchers = coroutinesTestRule.testDispatcherProvider, pixel = mockPixel ) @@ -212,15 +209,7 @@ class BrowserViewModelTest { } @Test - fun whenOpenShortcutIfUrlIsUseOurAppDomainThenFirePixel() { - val url = "http://m.$USE_OUR_APP_DOMAIN" - whenever(mockOmnibarEntryConverter.convertQueryToUrl(url)).thenReturn(url) - testee.onOpenShortcut(url) - verify(mockPixel).fire(AppPixelName.USE_OUR_APP_SHORTCUT_OPENED) - } - - @Test - fun whenOpenShortcutIfUrlIsNotUSeOurAppUrlThenFirePixel() { + fun whenOpenShortcutThenFirePixel() { val url = "example.com" whenever(mockOmnibarEntryConverter.convertQueryToUrl(url)).thenReturn(url) testee.onOpenShortcut(url) diff --git a/app/src/androidTest/java/com/duckduckgo/app/browser/logindetection/JsLoginDetectorTest.kt b/app/src/androidTest/java/com/duckduckgo/app/browser/logindetection/JsLoginDetectorTest.kt index 348df5a773e7..a829ac028eaf 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/browser/logindetection/JsLoginDetectorTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/browser/logindetection/JsLoginDetectorTest.kt @@ -23,8 +23,6 @@ import androidx.test.annotation.UiThreadTest import androidx.test.platform.app.InstrumentationRegistry import com.duckduckgo.app.CoroutineTestRule import com.duckduckgo.app.browser.logindetection.LoginDetectionJavascriptInterface.Companion.JAVASCRIPT_INTERFACE_NAME -import com.duckduckgo.app.global.events.db.UserEventsStore -import com.duckduckgo.app.global.useourapp.UseOurAppDetector import com.duckduckgo.app.runBlocking import com.duckduckgo.app.settings.db.SettingsDataStore import com.nhaarman.mockitokotlin2.* @@ -40,9 +38,7 @@ class JsLoginDetectorTest { var coroutinesTestRule = CoroutineTestRule() private val settingsDataStore: SettingsDataStore = mock() - private val userEventsStore: UserEventsStore = mock() - - private val testee = JsLoginDetector(settingsDataStore, UseOurAppDetector(userEventsStore)) + private val testee = JsLoginDetector(settingsDataStore) @UiThreadTest @Test diff --git a/app/src/androidTest/java/com/duckduckgo/app/browser/shortcut/ShortcutReceiverTest.kt b/app/src/androidTest/java/com/duckduckgo/app/browser/shortcut/ShortcutReceiverTest.kt index 1a796dba2d8d..cf077a92508e 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/browser/shortcut/ShortcutReceiverTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/browser/shortcut/ShortcutReceiverTest.kt @@ -18,18 +18,10 @@ package com.duckduckgo.app.browser.shortcut import android.content.Intent import com.duckduckgo.app.CoroutineTestRule -import com.duckduckgo.app.global.events.db.UserEventKey -import com.duckduckgo.app.global.events.db.UserEventsStore -import com.duckduckgo.app.global.useourapp.UseOurAppDetector import com.duckduckgo.app.pixels.AppPixelName -import com.duckduckgo.app.runBlocking -import com.duckduckgo.app.statistics.Variant -import com.duckduckgo.app.statistics.VariantManager import com.duckduckgo.app.statistics.pixels.Pixel import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.never import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Before import org.junit.Rule @@ -41,63 +33,16 @@ class ShortcutReceiverTest { @get:Rule var coroutinesTestRule = CoroutineTestRule() - private val mockUserEventsStore: UserEventsStore = mock() private val mockPixel: Pixel = mock() - private val mockVariantManager: VariantManager = mock() private lateinit var testee: ShortcutReceiver @Before fun before() { - testee = ShortcutReceiver(UseOurAppDetector(mockUserEventsStore), mockPixel, mockUserEventsStore, coroutinesTestRule.testDispatcherProvider, mockVariantManager) + testee = ShortcutReceiver(mockPixel, coroutinesTestRule.testDispatcherProvider) } @Test - fun whenIntentReceivedIfUrlIsFromUseOurAppDomainAndVariantIsInAppUsageThenRegisterTimestamp() = coroutinesTestRule.runBlocking { - setInAppUsageVariant() - val intent = Intent() - intent.putExtra(ShortcutBuilder.SHORTCUT_URL_ARG, "https://facebook.com") - intent.putExtra(ShortcutBuilder.SHORTCUT_TITLE_ARG, "Title") - testee.onReceive(null, intent) - - verify(mockUserEventsStore).registerUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED) - } - - @Test - fun whenIntentReceivedIfUrlIsFromUseOurAppDomainAndVariantIsNotInAppUsageThenDoNotRegisterTimestamp() = coroutinesTestRule.runBlocking { - setDefaultVariant() - val intent = Intent() - intent.putExtra(ShortcutBuilder.SHORTCUT_URL_ARG, "https://facebook.com") - intent.putExtra(ShortcutBuilder.SHORTCUT_TITLE_ARG, "Title") - testee.onReceive(null, intent) - - verify(mockUserEventsStore, never()).registerUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED) - } - - @Test - fun whenIntentReceivedIfUrlContainsUseOurAppDomainThenFirePixel() { - setDefaultVariant() - val intent = Intent() - intent.putExtra(ShortcutBuilder.SHORTCUT_URL_ARG, "https://facebook.com") - intent.putExtra(ShortcutBuilder.SHORTCUT_TITLE_ARG, "Title") - testee.onReceive(null, intent) - - verify(mockPixel).fire(AppPixelName.USE_OUR_APP_SHORTCUT_ADDED) - } - - @Test - fun whenIntentReceivedIfUrlIsNotFromUseOurAppDomainThenDoNotRegisterEvent() = coroutinesTestRule.runBlocking { - setDefaultVariant() - val intent = Intent() - intent.putExtra(ShortcutBuilder.SHORTCUT_URL_ARG, "www.example.com") - intent.putExtra(ShortcutBuilder.SHORTCUT_TITLE_ARG, "Title") - testee.onReceive(null, intent) - - verify(mockUserEventsStore, never()).registerUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED) - } - - @Test - fun whenIntentReceivedIfUrlIsNotFromUseOurAppDomainThenFireShortcutAddedPixel() { - setDefaultVariant() + fun whenIntentReceivedThenFireShortcutAddedPixel() { val intent = Intent() intent.putExtra(ShortcutBuilder.SHORTCUT_URL_ARG, "www.example.com") intent.putExtra(ShortcutBuilder.SHORTCUT_TITLE_ARG, "Title") @@ -105,22 +50,4 @@ class ShortcutReceiverTest { verify(mockPixel).fire(AppPixelName.SHORTCUT_ADDED) } - - private fun setDefaultVariant() { - whenever(mockVariantManager.getVariant()).thenReturn(VariantManager.DEFAULT_VARIANT) - } - - private fun setInAppUsageVariant() { - whenever(mockVariantManager.getVariant()).thenReturn( - Variant( - "test", - features = listOf( - VariantManager.VariantFeature.InAppUsage, - VariantManager.VariantFeature.RemoveDay1AndDay3Notifications, - VariantManager.VariantFeature.KillOnboarding - ), - filterBy = { true } - ) - ) - } } diff --git a/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaTest.kt b/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaTest.kt index ab8c8a927881..d37e74342585 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaTest.kt @@ -338,42 +338,6 @@ class CtaTest { assertEquals("FacebookwithZero", value) } - @Test - fun whenCtaIsUseOurAppReturnEmptyOkParameters() { - val testee = UseOurAppCta() - assertTrue(testee.pixelOkParameters().isEmpty()) - } - - @Test - fun whenCtaIsUseOurAppReturnEmptyCancelParameters() { - val testee = UseOurAppCta() - assertTrue(testee.pixelCancelParameters().isEmpty()) - } - - @Test - fun whenCtaIsUseOurAppReturnEmptyShownParameters() { - val testee = UseOurAppCta() - assertTrue(testee.pixelShownParameters().isEmpty()) - } - - @Test - fun whenCtaIsUseOurAppDeletionReturnEmptyOkParameters() { - val testee = UseOurAppDeletionCta() - assertTrue(testee.pixelOkParameters().isEmpty()) - } - - @Test - fun whenCtaIsUseOurAppDeletionReturnEmptyCancelParameters() { - val testee = UseOurAppDeletionCta() - assertTrue(testee.pixelCancelParameters().isEmpty()) - } - - @Test - fun whenCtaIsUseOurAppDeletionReturnEmptyShownParameters() { - val testee = UseOurAppDeletionCta() - assertTrue(testee.pixelShownParameters().isEmpty()) - } - @Test fun whenTryClearDataCtaShownThenConcatenateJourneyStoredValueInPixel() { val existingJourney = "s:0-t:1" diff --git a/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt index 9807c0de498c..069549b38fc3 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt @@ -28,13 +28,9 @@ import com.duckduckgo.app.cta.db.DismissedCtaDao import com.duckduckgo.app.cta.model.CtaId import com.duckduckgo.app.cta.model.DismissedCta import com.duckduckgo.app.global.db.AppDatabase -import com.duckduckgo.app.global.events.db.UserEventEntity -import com.duckduckgo.app.global.events.db.UserEventKey import com.duckduckgo.app.global.events.db.UserEventsStore import com.duckduckgo.app.global.install.AppInstallStore import com.duckduckgo.app.global.model.Site -import com.duckduckgo.app.global.useourapp.UseOurAppDetector -import com.duckduckgo.app.global.useourapp.UseOurAppDetector.Companion.USE_OUR_APP_SHORTCUT_URL import com.duckduckgo.app.onboarding.store.AppStage import com.duckduckgo.app.onboarding.store.OnboardingStore import com.duckduckgo.app.onboarding.store.UserStageStore @@ -160,12 +156,9 @@ class CtaViewModelTest { mockWidgetCapabilities, mockDismissedCtaDao, mockUserWhitelistDao, - mockVariantManager, mockSettingsDataStore, mockOnboardingStore, mockUserStageStore, - mockUserEventsStore, - UseOurAppDetector(mockUserEventsStore), mockTabRepository, coroutineRule.testDispatcherProvider ) @@ -497,91 +490,6 @@ class CtaViewModelTest { assertNull(value) } - @Test - fun whenRefreshCtaOnHomeTabAndUseOurAppOnboardingActiveThenUseOurAppCtaShown() = runBlockingTest { - givenUseOurAppActive() - - val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = false) - assertTrue(value is UseOurAppCta) - } - - @Test - fun whenRefreshCtaOnHomeTabAndUseOurAppOnboardingActiveAndHideTipsIsTrueThenReturnNull() = runBlockingTest { - givenUseOurAppActive() - whenever(mockSettingsDataStore.hideTips).thenReturn(true) - - val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = false) - assertNull(value) - } - - @Test - fun whenRefreshCtaOnHomeTabAndUseOurAppOnboardingActiveAndCtaShownThenReturnNull() = runBlockingTest { - givenUseOurAppActive() - whenever(mockDismissedCtaDao.exists(CtaId.USE_OUR_APP)).thenReturn(true) - - val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = false) - assertNull(value) - } - - @Test - fun whenRefreshCtaWhileBrowsingAndSiteIsUseOurAppAndTwoDaysSinceShortcutAddedThenShowUseOurAppDeletionCta() = runBlockingTest { - givenUserIsEstablished() - val timestampEntity = UserEventEntity(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2)) - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(timestampEntity) - - val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = true, site = site(url = USE_OUR_APP_SHORTCUT_URL)) - assertTrue(value is UseOurAppDeletionCta) - } - - @Test - fun whenRefreshCtaWhileBrowsingAndSiteIsUseOurAppAndTwoDaysSinceShortcutAddedAndHideTipsIsTrueThenReturnNull() = runBlockingTest { - givenUserIsEstablished() - val timestampEntity = UserEventEntity(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2)) - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(timestampEntity) - whenever(mockSettingsDataStore.hideTips).thenReturn(true) - - val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = true, site = site(url = USE_OUR_APP_SHORTCUT_URL)) - assertNull(value) - } - - @Test - fun whenRefreshCtaWhileBrowsingAndSiteIsUseOurAppAndTwoDaysSinceShortcutAddedAndCtaShownThenReturnNull() = runBlockingTest { - givenUserIsEstablished() - val timestampEntity = UserEventEntity(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2)) - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(timestampEntity) - whenever(mockDismissedCtaDao.exists(CtaId.USE_OUR_APP_DELETION)).thenReturn(true) - - val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = true, site = site(url = USE_OUR_APP_SHORTCUT_URL)) - assertNull(value) - } - - @Test - fun whenRefreshCtaWhileBrowsingAndSiteIsUseOurAppAndLessThanTwoDaysSinceShortcutAddedThenReturnNull() = runBlockingTest { - givenUserIsEstablished() - val timestampEntity = UserEventEntity(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)) - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(timestampEntity) - - val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = true, site = site(url = USE_OUR_APP_SHORTCUT_URL)) - assertNull(value) - } - - @Test - fun whenRefreshCtaWhileBrowsingAndSiteIsNotUseOurAppAndTwoDaysSinceShortcutAddedThenReturnNull() = runBlockingTest { - givenUserIsEstablished() - val timestampEntity = UserEventEntity(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2)) - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(timestampEntity) - - val value = testee.refreshCta(coroutineRule.testDispatcher, isBrowserShowing = true, site = site(url = "test")) - assertNull(value) - } - - @Test - fun whenUseOurAppCtaDismissedThenStageCompleted() = coroutineRule.runBlocking { - givenUseOurAppActive() - testee.onUserDismissedCta(UseOurAppCta()) - verify(mockUserStageStore).stageCompleted(AppStage.USE_OUR_APP_ONBOARDING) - } - @Test fun whenUserHidesAllTipsThenFireButtonAnimationShouldNotShow() = coroutineRule.runBlocking { givenOnboardingActive() @@ -612,7 +520,6 @@ class CtaViewModelTest { @Test fun whenFireButtonAnimationActiveAndUserOpensANewTabThenFireButtonAnimationStops() = coroutineRule.runBlocking { - val values = mutableListOf() givenOnboardingActive() db.tabsDao().insertTab(TabEntity(tabId = "0", position = 0)) db.dismissedCtaDao().insert(DismissedCta(CtaId.DAX_DIALOG_TRACKERS_FOUND)) @@ -755,10 +662,6 @@ class CtaViewModelTest { whenever(mockUserStageStore.getUserAppStage()).thenReturn(AppStage.DAX_ONBOARDING) } - private suspend fun givenUseOurAppActive() { - whenever(mockUserStageStore.getUserAppStage()).thenReturn(AppStage.USE_OUR_APP_ONBOARDING) - } - private fun site( url: String = "http://www.test.com", uri: Uri? = Uri.parse(url), diff --git a/app/src/androidTest/java/com/duckduckgo/app/global/db/AppDatabaseTest.kt b/app/src/androidTest/java/com/duckduckgo/app/global/db/AppDatabaseTest.kt index ac35357669ad..4a98dab81cc7 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/global/db/AppDatabaseTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/global/db/AppDatabaseTest.kt @@ -24,10 +24,8 @@ import androidx.room.testing.MigrationTestHelper import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation -import com.duckduckgo.app.browser.addtohome.AddToHomeCapabilityDetector import com.duckduckgo.app.global.exception.UncaughtExceptionSource import com.duckduckgo.app.onboarding.store.AppStage -import com.duckduckgo.app.settings.db.SettingsDataStore import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.mock @@ -47,11 +45,7 @@ class AppDatabaseTest { val testHelper = MigrationTestHelper(getInstrumentation(), AppDatabase::class.qualifiedName, FrameworkSQLiteOpenHelperFactory()) private val context = mock() - private val mockSettingsDataStore = mock() - private val mockAddToHomeCapabilityDetector = mock() - - private val migrationsProvider: MigrationsProvider = - MigrationsProvider(context, mockSettingsDataStore, mockAddToHomeCapabilityDetector) + private val migrationsProvider: MigrationsProvider = MigrationsProvider(context) @Before fun setup() { @@ -266,7 +260,7 @@ class AppDatabaseTest { @Test fun whenMigratingFromVersion22To23IfUserStageIsUseOurAppNotificationThenMigrateToEstablished() { testHelper.createDatabase(TEST_DB_NAME, 22).use { - givenUserStageIs(it, AppStage.USE_OUR_APP_NOTIFICATION) + it.execSQL("INSERT INTO `userStage` values (1, 'USE_OUR_APP_NOTIFICATION') ") testHelper.runMigrationsAndValidate(TEST_DB_NAME, 23, true, migrationsProvider.MIGRATION_22_TO_23) val stage = getUserStage(it) diff --git a/app/src/androidTest/java/com/duckduckgo/app/global/events/db/UserEventsDaoTest.kt b/app/src/androidTest/java/com/duckduckgo/app/global/events/db/UserEventsDaoTest.kt index 1e82c01385a8..e819c3732e59 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/global/events/db/UserEventsDaoTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/global/events/db/UserEventsDaoTest.kt @@ -58,23 +58,23 @@ class UserEventsDaoTest { @Test fun whenGetUserEventAndDatabaseEmptyThenReturnNull() = coroutineRule.runBlocking { - assertNull(testee.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)) + assertNull(testee.getUserEvent(UserEventKey.FIRE_BUTTON_EXECUTED)) } @Test fun whenInsertingUserEventThenTimestampIsNotNull() = coroutineRule.runBlocking { - testee.registerUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED) + testee.registerUserEvent(UserEventKey.FIRE_BUTTON_EXECUTED) - assertNotNull(testee.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)?.timestamp) + assertNotNull(testee.getUserEvent(UserEventKey.FIRE_BUTTON_EXECUTED)?.timestamp) } @Test fun whenInsertingSameUserEventThenReplaceOldTimestamp() = coroutineRule.runBlocking { - testee.registerUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED) - val timestamp = testee.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)?.timestamp + testee.registerUserEvent(UserEventKey.FIRE_BUTTON_EXECUTED) + val timestamp = testee.getUserEvent(UserEventKey.FIRE_BUTTON_EXECUTED)?.timestamp - testee.registerUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED) + testee.registerUserEvent(UserEventKey.FIRE_BUTTON_EXECUTED) - assertNotEquals(timestamp, testee.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)?.timestamp) + assertNotEquals(timestamp, testee.getUserEvent(UserEventKey.FIRE_BUTTON_EXECUTED)?.timestamp) } } diff --git a/app/src/androidTest/java/com/duckduckgo/app/global/useourapp/UseOurAppDetectorTest.kt b/app/src/androidTest/java/com/duckduckgo/app/global/useourapp/UseOurAppDetectorTest.kt deleted file mode 100644 index 5e69dd985b64..000000000000 --- a/app/src/androidTest/java/com/duckduckgo/app/global/useourapp/UseOurAppDetectorTest.kt +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2020 DuckDuckGo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.duckduckgo.app.global.useourapp - -import android.webkit.WebView -import com.duckduckgo.app.CoroutineTestRule -import com.duckduckgo.app.browser.logindetection.WebNavigationEvent -import com.duckduckgo.app.global.events.db.UserEventEntity -import com.duckduckgo.app.global.events.db.UserEventKey -import com.duckduckgo.app.global.events.db.UserEventsStore -import com.duckduckgo.app.runBlocking -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.never -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever -import kotlinx.coroutines.ExperimentalCoroutinesApi -import org.junit.Assert.* -import org.junit.Before -import org.junit.Rule -import org.junit.Test - -@ExperimentalCoroutinesApi -class UseOurAppDetectorTest { - - @get:Rule - var coroutineRule = CoroutineTestRule() - - private lateinit var testee: UseOurAppDetector - - private val mockUserEventsStore: UserEventsStore = mock() - - @Before - fun setup() { - testee = UseOurAppDetector(mockUserEventsStore) - } - - @Test - fun whenCheckingIfUrlIsFromUseOurAppDomainThenReturnTrue() { - assertTrue(testee.isUseOurAppUrl("http://www.facebook.com")) - } - - @Test - fun whenCheckingIfMobileUrlIsFromUseOurAppDomainThenReturnTrue() { - assertTrue(testee.isUseOurAppUrl("http://m.facebook.com")) - } - - @Test - fun whenCheckingIfMobileOnlyDomainIsFromUseOurAppDomainThenReturnTrue() { - assertTrue(testee.isUseOurAppUrl("m.facebook.com")) - } - - @Test - fun whenCheckingIfOnlyDomainUrlIsFromUseOurAppDomainThenReturnTrue() { - assertTrue(testee.isUseOurAppUrl("facebook.com")) - } - - @Test - fun whenCheckingIfUrlIsFromUseOurAppDomainThenReturnFalse() { - assertFalse(testee.isUseOurAppUrl("http://example.com")) - } - - @Test - fun whenAllowLoginDetectionAndShortcutNotAddedThenReturnFalse() = coroutineRule.runBlocking { - val webView: WebView = mock() - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(null) - - assertFalse(testee.allowLoginDetection(WebNavigationEvent.OnPageStarted(webView))) - } - - @Test - fun whenAllowLoginDetectionAndFireProofAlreadySeenThenReturnFalse() = coroutineRule.runBlocking { - val webView: WebView = mock() - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(UserEventEntity(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)) - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN)).thenReturn(UserEventEntity(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN)) - - assertFalse(testee.allowLoginDetection(WebNavigationEvent.OnPageStarted(webView))) - } - - @Test - fun whenAllowLoginDetectionWithOnPageStartedEventAndUrlIsUseOurAppThenReturnTrue() = coroutineRule.runBlocking { - val webView: WebView = mock() - whenever(webView.url).thenReturn("http://m.facebook.com") - givenShortcutIsAddedAndFireproofNotSeen() - - assertTrue(testee.allowLoginDetection(WebNavigationEvent.OnPageStarted(webView))) - } - - @Test - fun whenAllowLoginDetectionWithOnPageStartedEventAndUrlIsNotUseOurAppThenReturnFalse() = coroutineRule.runBlocking { - val webView: WebView = mock() - whenever(webView.url).thenReturn("http://example.com") - givenShortcutIsAddedAndFireproofNotSeen() - - assertFalse(testee.allowLoginDetection(WebNavigationEvent.OnPageStarted(webView))) - } - - @Test - fun whenAllowLoginDetectionWithShouldInterceptEventAndUrlIsUseOurAppThenReturnTrue() = coroutineRule.runBlocking { - val webView: WebView = mock() - whenever(webView.url).thenReturn("http://m.facebook.com") - givenShortcutIsAddedAndFireproofNotSeen() - - assertTrue(testee.allowLoginDetection(WebNavigationEvent.ShouldInterceptRequest(webView, mock()))) - } - - @Test - fun whenAllowLoginDetectionWithOnShouldInterceptEventAndUrlIsNotUseOurAppThenReturnFalse() = coroutineRule.runBlocking { - val webView: WebView = mock() - whenever(webView.url).thenReturn("http://example.com") - givenShortcutIsAddedAndFireproofNotSeen() - - assertFalse(testee.allowLoginDetection(WebNavigationEvent.ShouldInterceptRequest(webView, mock()))) - } - - @Test - fun whenRegisterIfFireproofSeenForTheFirstTimeAndUrlIsUseOurAppThenRegisterUserEvent() = coroutineRule.runBlocking { - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN)).thenReturn(null) - - testee.registerIfFireproofSeenForTheFirstTime("http://m.facebook.com") - - verify(mockUserEventsStore).registerUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN) - } - - @Test - fun whenRegisterIfFireproofSeenForTheFirstTimeAndUrlIsNotUseOurAppThenDoNotRegisterUserEvent() = coroutineRule.runBlocking { - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN)).thenReturn(null) - - testee.registerIfFireproofSeenForTheFirstTime("example.com") - - verify(mockUserEventsStore, never()).registerUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN) - } - - @Test - fun whenRegisterIfFireproofSeenForTheFirstTimeButAlreadySeenThenDoNotRegisterUserEvent() = coroutineRule.runBlocking { - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN)).thenReturn(UserEventEntity(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN)) - - testee.registerIfFireproofSeenForTheFirstTime("http://m.facebook.com") - - verify(mockUserEventsStore, never()).registerUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN) - } - - private suspend fun givenShortcutIsAddedAndFireproofNotSeen() { - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)).thenReturn(UserEventEntity(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED)) - whenever(mockUserEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN)).thenReturn(null) - } -} diff --git a/app/src/androidTest/java/com/duckduckgo/app/notification/AndroidNotificationSchedulerTest.kt b/app/src/androidTest/java/com/duckduckgo/app/notification/AndroidNotificationSchedulerTest.kt index b6569b913028..dbc9e324fde2 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/notification/AndroidNotificationSchedulerTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/notification/AndroidNotificationSchedulerTest.kt @@ -26,23 +26,17 @@ import androidx.work.WorkManager import androidx.work.impl.utils.SynchronousExecutor import androidx.work.testing.WorkManagerTestInitHelper import com.duckduckgo.app.CoroutineTestRule -import com.duckduckgo.app.global.install.AppInstallStore import com.duckduckgo.app.notification.NotificationScheduler.ClearDataNotificationWorker import com.duckduckgo.app.notification.NotificationScheduler.PrivacyNotificationWorker import com.duckduckgo.app.notification.model.SchedulableNotification -import com.duckduckgo.app.statistics.Variant -import com.duckduckgo.app.statistics.VariantManager -import com.duckduckgo.app.statistics.VariantManager.Companion.DEFAULT_VARIANT import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.whenever import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking -import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test -import java.util.concurrent.TimeUnit import kotlin.reflect.jvm.jvmName class AndroidNotificationSchedulerTest { @@ -53,9 +47,6 @@ class AndroidNotificationSchedulerTest { private val clearNotification: SchedulableNotification = mock() private val privacyNotification: SchedulableNotification = mock() - private val useOurAppNotification: SchedulableNotification = mock() - private val variantManager: VariantManager = mock() - private val appInstallStore: AppInstallStore = mock() private val context = InstrumentationRegistry.getInstrumentation().targetContext private lateinit var workManager: WorkManager @@ -68,10 +59,7 @@ class AndroidNotificationSchedulerTest { testee = NotificationScheduler( workManager, clearNotification, - privacyNotification, - useOurAppNotification, - variantManager, - appInstallStore + privacyNotification ) } @@ -88,7 +76,6 @@ class AndroidNotificationSchedulerTest { @Test fun whenPrivacyNotificationClearDataCanShowThenPrivacyNotificationIsScheduled() = runBlocking { - setDefaultVariant() whenever(privacyNotification.canShow()).thenReturn(true) whenever(clearNotification.canShow()).thenReturn(true) testee.scheduleNextNotification() @@ -98,7 +85,6 @@ class AndroidNotificationSchedulerTest { @Test fun whenPrivacyNotificationCanShowButClearDataCannotThenPrivacyNotificationIsScheduled() = runBlocking { - setDefaultVariant() whenever(privacyNotification.canShow()).thenReturn(true) whenever(clearNotification.canShow()).thenReturn(false) testee.scheduleNextNotification() @@ -108,7 +94,6 @@ class AndroidNotificationSchedulerTest { @Test fun whenPrivacyNotificationCannotShowAndClearNotificationCanShowThenClearNotificationIsScheduled() = runBlocking { - setDefaultVariant() whenever(privacyNotification.canShow()).thenReturn(false) whenever(clearNotification.canShow()).thenReturn(true) testee.scheduleNextNotification() @@ -118,7 +103,6 @@ class AndroidNotificationSchedulerTest { @Test fun whenPrivacyNotificationAndClearNotificationCannotShowThenNoNotificationScheduled() = runBlocking { - setDefaultVariant() whenever(privacyNotification.canShow()).thenReturn(false) whenever(clearNotification.canShow()).thenReturn(false) testee.scheduleNextNotification() @@ -126,189 +110,6 @@ class AndroidNotificationSchedulerTest { assertNoNotificationScheduled() } - @Test - fun whenInAppUsageVariantAndUseOurAppNotificationCanShowThenNotificationScheduled() = runBlocking { - givenNoInactiveUserNotifications() - setInAppUsageVariant() - whenever(useOurAppNotification.canShow()).thenReturn(true) - - testee.scheduleNextNotification() - - assertNotificationScheduled(NotificationScheduler.UseOurAppNotificationWorker::class.jvmName, NotificationScheduler.USE_OUR_APP_WORK_REQUEST_TAG) - } - - @Test - fun whenInAppUsageVariantUseOurAppNotificationCannotShowThenNoNotificationScheduled() = runBlocking { - givenNoInactiveUserNotifications() - setInAppUsageVariant() - whenever(useOurAppNotification.canShow()).thenReturn(false) - - testee.scheduleNextNotification() - - assertNoNotificationScheduled(NotificationScheduler.USE_OUR_APP_WORK_REQUEST_TAG) - } - - @Test - fun whenInAppUsageSecondControlVariantThenNoNotificationScheduled() = runBlocking { - setInAppUsageSecondControlVariant() - whenever(useOurAppNotification.canShow()).thenReturn(true) - - testee.scheduleNextNotification() - - assertNoNotificationScheduled(NotificationScheduler.USE_OUR_APP_WORK_REQUEST_TAG) - } - - @Test - fun whenInAppUsageControlVariantThenNoNotificationScheduled() = runBlocking { - givenNoInactiveUserNotifications() - setInAppUsageControlVariant() - whenever(useOurAppNotification.canShow()).thenReturn(true) - - testee.scheduleNextNotification() - - assertNoNotificationScheduled(NotificationScheduler.USE_OUR_APP_WORK_REQUEST_TAG) - } - - @Test - fun whenInAppUsageControlVariantAndPrivacyNotificationClearDataCanShowThenPrivacyNotificationIsScheduled() = runBlocking { - setInAppUsageControlVariant() - whenever(privacyNotification.canShow()).thenReturn(true) - whenever(clearNotification.canShow()).thenReturn(true) - testee.scheduleNextNotification() - - assertNotificationScheduled(PrivacyNotificationWorker::class.jvmName) - } - - @Test - fun whenInAppUsageControlVariantAndPrivacyNotificationCanShowButClearDataCannotThenPrivacyNotificationIsScheduled() = runBlocking { - setInAppUsageControlVariant() - whenever(privacyNotification.canShow()).thenReturn(true) - whenever(clearNotification.canShow()).thenReturn(false) - testee.scheduleNextNotification() - - assertNotificationScheduled(PrivacyNotificationWorker::class.jvmName) - } - - @Test - fun whenInAppUsageControlVariantAndPrivacyNotificationCannotShowAndClearNotificationCanShowThenClearNotificationScheduled() = runBlocking { - setInAppUsageControlVariant() - whenever(privacyNotification.canShow()).thenReturn(false) - whenever(clearNotification.canShow()).thenReturn(true) - testee.scheduleNextNotification() - - assertNotificationScheduled(ClearDataNotificationWorker::class.jvmName) - } - - @Test - fun whenInAppUsageControlVariantAndPrivacyNotificationAndClearNotificationCannotShowThenNoNotificationScheduled() = runBlocking { - setDefaultVariant() - whenever(privacyNotification.canShow()).thenReturn(false) - whenever(clearNotification.canShow()).thenReturn(false) - testee.scheduleNextNotification() - - assertNoNotificationScheduled() - } - - @Test - fun whenInAppVariantAndGetDurationForInactiveNotificationForDay1AndAppHasBeenInstalled0DaysThenReturn1() { - setInAppUsageVariant() - givenAppHasBeenInstalledForDays(days = 0) - - assertEquals(1, testee.getDurationForInactiveNotification(1)) - } - - @Test - fun whenInAppVariantAndGetDurationForInactiveNotificationForDay1AndAppHasBeenInstalled1DayThenReturn1() { - setInAppUsageVariant() - givenAppHasBeenInstalledForDays(days = 1) - - assertEquals(1, testee.getDurationForInactiveNotification(1)) - } - - @Test - fun whenInAppVariantAndGetDurationForInactiveNotificationForDay1AndAppHasBeenInstalled2DaysThenReturn2() { - setInAppUsageVariant() - givenAppHasBeenInstalledForDays(days = 2) - - assertEquals(2, testee.getDurationForInactiveNotification(1)) - } - - @Test - fun whenInAppVariantAndGetDurationForInactiveNotificationForDay3AndAppHasBeenInstalled0DaysThenReturn4() { - setInAppUsageVariant() - givenAppHasBeenInstalledForDays(days = 0) - - assertEquals(4, testee.getDurationForInactiveNotification(3)) - } - - @Test - fun whenInAppVariantAndGetDurationForInactiveNotificationForDay3AndAppHasBeenInstalled1DayThenReturn3() { - setInAppUsageVariant() - givenAppHasBeenInstalledForDays(days = 1) - - assertEquals(3, testee.getDurationForInactiveNotification(3)) - } - - @Test - fun whenDefaultVariantAndGetDurationForInactiveNotificationForDay1AndAppHasBeenInstalled2DaysThenReturn1() { - setDefaultVariant() - givenAppHasBeenInstalledForDays(days = 2) - - assertEquals(1, testee.getDurationForInactiveNotification(1)) - } - - @Test - fun whenDefaultVariantAndGetDurationForInactiveNotificationForDay3AndAppHasBeenInstalled0DaysThenReturn3() { - setDefaultVariant() - givenAppHasBeenInstalledForDays(days = 0) - - assertEquals(3, testee.getDurationForInactiveNotification(3)) - } - - private fun givenAppHasBeenInstalledForDays(days: Long) { - val timeSinceInstallation = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(days) - whenever(appInstallStore.installTimestamp).thenReturn(timeSinceInstallation) - } - - private suspend fun givenNoInactiveUserNotifications() { - whenever(privacyNotification.canShow()).thenReturn(false) - whenever(clearNotification.canShow()).thenReturn(false) - } - - private fun setInAppUsageVariant() { - whenever(variantManager.getVariant()).thenReturn( - Variant( - "test", - features = listOf( - VariantManager.VariantFeature.InAppUsage, - VariantManager.VariantFeature.KillOnboarding - ), - filterBy = { true } - ) - ) - } - - private fun setInAppUsageSecondControlVariant() { - whenever(variantManager.getVariant()).thenReturn( - Variant( - "test", - features = listOf( - VariantManager.VariantFeature.RemoveDay1AndDay3Notifications, - VariantManager.VariantFeature.KillOnboarding - ), - filterBy = { true } - ) - ) - } - - private fun setInAppUsageControlVariant() { - whenever(variantManager.getVariant()).thenReturn(Variant("test", features = emptyList(), filterBy = { true })) - } - - private fun setDefaultVariant() { - whenever(variantManager.getVariant()).thenReturn(DEFAULT_VARIANT) - } - private fun assertNotificationScheduled(workerName: String, tag: String = NotificationScheduler.UNUSED_APP_WORK_REQUEST_TAG) { assertTrue(getScheduledWorkers(tag).any { it.tags.contains(workerName) }) } diff --git a/app/src/androidTest/java/com/duckduckgo/app/notification/NotificationHandlerServiceTest.kt b/app/src/androidTest/java/com/duckduckgo/app/notification/NotificationHandlerServiceTest.kt index 43c7eac4e8eb..01ab33d01b63 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/notification/NotificationHandlerServiceTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/notification/NotificationHandlerServiceTest.kt @@ -23,10 +23,6 @@ import com.duckduckgo.app.CoroutineTestRule import com.duckduckgo.app.notification.NotificationHandlerService.Companion.PIXEL_SUFFIX_EXTRA import com.duckduckgo.app.notification.NotificationHandlerService.NotificationEvent.CANCEL import com.duckduckgo.app.notification.NotificationHandlerService.NotificationEvent.CLEAR_DATA_LAUNCH -import com.duckduckgo.app.notification.NotificationHandlerService.NotificationEvent.USE_OUR_APP -import com.duckduckgo.app.onboarding.store.AppStage -import com.duckduckgo.app.onboarding.store.UserStageStore -import com.duckduckgo.app.runBlocking import com.duckduckgo.app.statistics.pixels.Pixel import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.eq @@ -44,7 +40,6 @@ class NotificationHandlerServiceTest { var coroutinesTestRule = CoroutineTestRule() private var mockPixel: Pixel = mock() - private var mockUserStageStore: UserStageStore = mock() private var testee = NotificationHandlerService() private val context = InstrumentationRegistry.getInstrumentation().targetContext @@ -53,7 +48,6 @@ class NotificationHandlerServiceTest { testee.pixel = mockPixel testee.context = context testee.notificationManager = NotificationManagerCompat.from(context) - testee.userStageStore = mockUserStageStore testee.dispatcher = coroutinesTestRule.testDispatcherProvider } @@ -74,22 +68,4 @@ class NotificationHandlerServiceTest { testee.onHandleIntent(intent) verify(mockPixel).fire(eq("mnot_c_abc"), any(), any()) } - - @Test - fun whenIntentIsUseOurAppThenCorrespondingPixelIsFired() { - val intent = Intent(context, NotificationHandlerService::class.java) - intent.type = USE_OUR_APP - intent.putExtra(PIXEL_SUFFIX_EXTRA, "abc") - testee.onHandleIntent(intent) - verify(mockPixel).fire(eq("mnot_l_abc"), any(), any()) - } - - @Test - fun whenIntentIsUseOurAppThenRegisterInUseOurAppOnboardingStage() = coroutinesTestRule.runBlocking { - val intent = Intent(context, NotificationHandlerService::class.java) - intent.type = USE_OUR_APP - intent.putExtra(PIXEL_SUFFIX_EXTRA, "abc") - testee.onHandleIntent(intent) - verify(mockUserStageStore).moveToStage(AppStage.USE_OUR_APP_ONBOARDING) - } } diff --git a/app/src/androidTest/java/com/duckduckgo/app/notification/model/UseOurAppNotificationTest.kt b/app/src/androidTest/java/com/duckduckgo/app/notification/model/UseOurAppNotificationTest.kt deleted file mode 100644 index 7a94b3398881..000000000000 --- a/app/src/androidTest/java/com/duckduckgo/app/notification/model/UseOurAppNotificationTest.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2020 DuckDuckGo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.duckduckgo.app.notification.model - -import androidx.test.platform.app.InstrumentationRegistry -import com.duckduckgo.app.browser.addtohome.AddToHomeCapabilityDetector -import com.duckduckgo.app.notification.db.NotificationDao -import com.duckduckgo.app.settings.db.SettingsDataStore -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.whenever -import kotlinx.coroutines.runBlocking -import org.junit.Assert.* -import org.junit.Before -import org.junit.Test - -class UseOurAppNotificationTest { - - private val context = InstrumentationRegistry.getInstrumentation().targetContext - private val notificationsDao: NotificationDao = mock() - private val mockSettingsDataStore: SettingsDataStore = mock() - private val mockAddToHomeCapabilityDetector: AddToHomeCapabilityDetector = mock() - - private lateinit var testee: UseOurAppNotification - - @Before - fun before() { - testee = UseOurAppNotification(context, notificationsDao, mockSettingsDataStore, mockAddToHomeCapabilityDetector) - } - - @Test - fun whenNotificationNotSeenAndHideTipsIsFalseAndAddToHomeSupportedThenCanShowIsTrue() = runBlocking { - whenever(mockAddToHomeCapabilityDetector.isAddToHomeSupported()).thenReturn(true) - whenever(notificationsDao.exists(any())).thenReturn(false) - whenever(mockSettingsDataStore.hideTips).thenReturn(false) - assertTrue(testee.canShow()) - } - - @Test - fun whenNotificationAlreadySeenThenCanShowIsFalse() = runBlocking { - whenever(notificationsDao.exists(any())).thenReturn(true) - whenever(mockAddToHomeCapabilityDetector.isAddToHomeSupported()).thenReturn(true) - whenever(mockSettingsDataStore.hideTips).thenReturn(false) - assertFalse(testee.canShow()) - } - - @Test - fun whenHideTipsIsTrueThenCanShowIsFalse() = runBlocking { - whenever(notificationsDao.exists(any())).thenReturn(false) - whenever(mockAddToHomeCapabilityDetector.isAddToHomeSupported()).thenReturn(true) - whenever(mockSettingsDataStore.hideTips).thenReturn(true) - assertFalse(testee.canShow()) - } - - @Test - fun whenAddToHomeNotSupportedThenCanShowIsFalse() = runBlocking { - whenever(notificationsDao.exists(any())).thenReturn(false) - whenever(mockAddToHomeCapabilityDetector.isAddToHomeSupported()).thenReturn(false) - whenever(mockSettingsDataStore.hideTips).thenReturn(false) - assertFalse(testee.canShow()) - } -} diff --git a/app/src/androidTest/java/com/duckduckgo/app/onboarding/store/AppUserStageStoreTest.kt b/app/src/androidTest/java/com/duckduckgo/app/onboarding/store/AppUserStageStoreTest.kt index 6224b3ab5585..f314c2c32a3a 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/onboarding/store/AppUserStageStoreTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/onboarding/store/AppUserStageStoreTest.kt @@ -17,21 +17,14 @@ package com.duckduckgo.app.onboarding.store import com.duckduckgo.app.CoroutineTestRule -import com.duckduckgo.app.global.install.AppInstallStore import com.duckduckgo.app.runBlocking -import com.duckduckgo.app.statistics.Variant -import com.duckduckgo.app.statistics.VariantManager -import com.duckduckgo.app.statistics.VariantManager.Companion.DEFAULT_VARIANT -import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.never import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test -import java.util.concurrent.TimeUnit @ExperimentalCoroutinesApi class AppUserStageStoreTest { @@ -40,10 +33,7 @@ class AppUserStageStoreTest { var coroutineRule = CoroutineTestRule() private val userStageDao: UserStageDao = mock() - private val variantManager: VariantManager = mock() - private val appInstallStore: AppInstallStore = mock() - - private val testee = AppUserStageStore(userStageDao, coroutineRule.testDispatcherProvider, variantManager, appInstallStore) + private val testee = AppUserStageStore(userStageDao, coroutineRule.testDispatcherProvider) @Test fun whenGetUserAppStageThenReturnCurrentStage() = coroutineRule.runBlocking { @@ -72,24 +62,6 @@ class AppUserStageStoreTest { assertEquals(AppStage.ESTABLISHED, nextStage) } - @Test - fun whenStageUseOurAppNotificationCompletedThenStageEstablishedReturned() = coroutineRule.runBlocking { - givenCurrentStage(AppStage.USE_OUR_APP_NOTIFICATION) - - val nextStage = testee.stageCompleted(AppStage.USE_OUR_APP_NOTIFICATION) - - assertEquals(AppStage.ESTABLISHED, nextStage) - } - - @Test - fun whenStageUseOurAppOnboardingCompletedThenStageEstablishedReturned() = coroutineRule.runBlocking { - givenCurrentStage(AppStage.USE_OUR_APP_ONBOARDING) - - val nextStage = testee.stageCompleted(AppStage.USE_OUR_APP_ONBOARDING) - - assertEquals(AppStage.ESTABLISHED, nextStage) - } - @Test fun whenStageEstablishedCompletedThenStageEstablishedReturned() = coroutineRule.runBlocking { givenCurrentStage(AppStage.ESTABLISHED) @@ -101,56 +73,8 @@ class AppUserStageStoreTest { @Test fun whenMoveToStageThenUpdateUserStageInDao() = coroutineRule.runBlocking { - testee.moveToStage(AppStage.USE_OUR_APP_ONBOARDING) - verify(userStageDao).updateUserStage(AppStage.USE_OUR_APP_ONBOARDING) - } - - @Test - fun whenAppResumedAndInstalledFor3DaysAndKillOnboardingFeatureNotActiveIfUserInOnboardingThenDoNotUpdateUserStage() = coroutineRule.runBlocking { - givenDefaultVariant() - givenCurrentStage(AppStage.DAX_ONBOARDING) - givenAppInstalledByDays(days = 3) - - testee.onAppResumed() - - verify(userStageDao, never()).updateUserStage(AppStage.ESTABLISHED) - } - - @Test - fun whenAppResumedAndInstalledFor3DaysAndKillOnboardingFeatureActiveIfUserInOnboardingThenMoveToEstablished() = coroutineRule.runBlocking { - givenKillOnboardingFeature() - givenCurrentStage(AppStage.DAX_ONBOARDING) - givenAppInstalledByDays(days = 3) - - testee.onAppResumed() - - verify(userStageDao).updateUserStage(AppStage.ESTABLISHED) - } - - @Test - fun whenAppResumedAndInstalledForLess3DaysAndKillOnboardingFeatureActiveThenDoNotUpdateUserStage() = coroutineRule.runBlocking { - givenKillOnboardingFeature() - givenCurrentStage(AppStage.DAX_ONBOARDING) - givenAppInstalledByDays(days = 2) - - testee.onAppResumed() - - verify(userStageDao, never()).updateUserStage(any()) - } - - private fun givenAppInstalledByDays(days: Long) { - whenever(appInstallStore.hasInstallTimestampRecorded()).thenReturn(true) - whenever(appInstallStore.installTimestamp).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(days) - 1) - } - - private fun givenDefaultVariant() { - whenever(variantManager.getVariant(any())).thenReturn(DEFAULT_VARIANT) - } - - private fun givenKillOnboardingFeature() { - whenever(variantManager.getVariant()).thenReturn( - Variant("test", features = listOf(VariantManager.VariantFeature.KillOnboarding), filterBy = { true }) - ) + testee.moveToStage(AppStage.DAX_ONBOARDING) + verify(userStageDao).updateUserStage(AppStage.DAX_ONBOARDING) } private suspend fun givenCurrentStage(appStage: AppStage) { diff --git a/app/src/androidTest/java/com/duckduckgo/app/statistics/VariantManagerTest.kt b/app/src/androidTest/java/com/duckduckgo/app/statistics/VariantManagerTest.kt index 11e50781d205..c86a73eef782 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/statistics/VariantManagerTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/statistics/VariantManagerTest.kt @@ -17,7 +17,6 @@ package com.duckduckgo.app.statistics import com.duckduckgo.app.statistics.VariantManager.Companion.DEFAULT_VARIANT -import com.duckduckgo.app.statistics.VariantManager.VariantFeature.* import org.junit.Assert.* import org.junit.Test @@ -43,49 +42,6 @@ class VariantManagerTest { assertEquals(0, variant.features.size) } - // Use our app experiment - @Test - fun inBrowserControlVariantHasExpectedWeightAndNoFeatures() { - val variant = variants.first { it.key == "ma" } - assertEqualsDouble(0.0, variant.weight) - assertEquals(0, variant.features.size) - } - - @Test - fun inBrowserSecondControlVariantHasExpectedWeightAndRemoveDay1And3NotificationsAndKillOnboardingFeatures() { - val variant = variants.first { it.key == "mb" } - assertEqualsDouble(0.0, variant.weight) - assertEquals(2, variant.features.size) - assertTrue(variant.hasFeature(KillOnboarding)) - assertTrue(variant.hasFeature(RemoveDay1AndDay3Notifications)) - } - - @Test - fun inBrowserInAppUsageVariantHasExpectedWeightAndRemoveDay1And3NotificationsAndKillOnboardingAndInAppUsageFeatures() { - val variant = variants.first { it.key == "mc" } - assertEqualsDouble(0.0, variant.weight) - assertEquals(3, variant.features.size) - assertTrue(variant.hasFeature(KillOnboarding)) - assertTrue(variant.hasFeature(RemoveDay1AndDay3Notifications)) - assertTrue(variant.hasFeature(InAppUsage)) - } - - @Test - fun newInBrowserControlVariantHasExpectedWeightAndNoFeatures() { - val variant = variants.first { it.key == "zx" } - assertEqualsDouble(1.0, variant.weight) - assertEquals(0, variant.features.size) - } - - @Test - fun newInBrowserInAppUsageVariantHasExpectedWeightAndKillOnboardingAndInAppUsageFeatures() { - val variant = variants.first { it.key == "zy" } - assertEqualsDouble(1.0, variant.weight) - assertEquals(2, variant.features.size) - assertTrue(variant.hasFeature(KillOnboarding)) - assertTrue(variant.hasFeature(InAppUsage)) - } - @Test fun verifyNoDuplicateVariantNames() { val existingNames = mutableSetOf() diff --git a/app/src/androidTest/java/com/duckduckgo/app/systemsearch/SystemSearchViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/systemsearch/SystemSearchViewModelTest.kt index c4d614b7d70a..4e86ab9d834a 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/systemsearch/SystemSearchViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/systemsearch/SystemSearchViewModelTest.kt @@ -281,7 +281,7 @@ class SystemSearchViewModelTest { override suspend fun currentUserAppStage() = UserStage(appStage = AppStage.NEW) override fun insert(userStage: UserStage) {} } - return AppUserStageStore(emptyUserStageDao, coroutineRule.testDispatcherProvider, mock(), mock()) + return AppUserStageStore(emptyUserStageDao, coroutineRule.testDispatcherProvider) } companion object { diff --git a/app/src/androidTest/java/com/duckduckgo/app/tabs/model/TabDataRepositoryTest.kt b/app/src/androidTest/java/com/duckduckgo/app/tabs/model/TabDataRepositoryTest.kt index 53615eaf7401..54c4d310df46 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/tabs/model/TabDataRepositoryTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/tabs/model/TabDataRepositoryTest.kt @@ -30,25 +30,24 @@ import com.duckduckgo.app.browser.tabpreview.WebViewPreviewPersister import com.duckduckgo.app.global.db.AppDatabase import com.duckduckgo.app.global.events.db.UserEventsStore import com.duckduckgo.app.global.model.SiteFactory -import com.duckduckgo.app.global.useourapp.UseOurAppDetector -import com.duckduckgo.app.global.useourapp.UseOurAppDetector.Companion.USE_OUR_APP_DOMAIN import com.duckduckgo.app.privacy.model.PrivacyPractices import com.duckduckgo.app.tabs.db.TabsDao import com.duckduckgo.app.trackerdetection.EntityLookup import com.nhaarman.mockitokotlin2.* import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestCoroutineScope import org.junit.After import org.junit.Assert.* import org.junit.Before import org.junit.Rule import org.junit.Test +@ExperimentalCoroutinesApi class TabDataRepositoryTest { @get:Rule @@ -73,8 +72,6 @@ class TabDataRepositoryTest { private val mockUserEventsStore: UserEventsStore = mock() - private val useOurAppDetector = UseOurAppDetector(mockUserEventsStore) - private lateinit var testee: TabDataRepository private val mockFaviconManager: FaviconManager = mock() @@ -277,33 +274,6 @@ class TabDataRepositoryTest { db.close() } - @Test - fun whenSelectByUrlOrNewTabIfUrlAlreadyExistedInATabAndMatchesTheUseOurAppDomainThenSelectTheTab() = runBlocking { - val db = createDatabase() - val dao = db.tabsDao() - dao.insertTab(TabEntity(tabId = "id", url = "http://www.$USE_OUR_APP_DOMAIN/test", skipHome = false, viewed = true, position = 0)) - testee = tabDataRepository(dao) - - testee.selectByUrlOrNewTab("http://m.$USE_OUR_APP_DOMAIN") - - val value = testee.liveSelectedTab.blockingObserve()?.tabId - assertEquals("id", value) - db.close() - } - - @Test - fun whenSelectByUrlOrNewTabIfUrlNotExistedInATabAndUrlMatchesUseOurAppDomainThenAddNewTabWithCorrectUrl() = runBlocking { - val db = createDatabase() - val dao = db.tabsDao() - testee = tabDataRepository(dao) - - testee.selectByUrlOrNewTab("http://m.$USE_OUR_APP_DOMAIN") - - val value = testee.liveSelectedTab.blockingObserve()?.url - assertEquals("http://m.$USE_OUR_APP_DOMAIN", value) - db.close() - } - @Test fun whenAddFromSourceTabEnsureTabEntryContainsExpectedSourceId() = runBlocking { val db = createDatabase() @@ -450,8 +420,7 @@ class TabDataRepositoryTest { SiteFactory(mockPrivacyPractices, mockEntityLookup), mockWebViewPreviewPersister, mockFaviconManager, - useOurAppDetector, - GlobalScope + TestCoroutineScope() ) } diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt index 974884bbe8af..52d070d47d59 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt @@ -77,9 +77,6 @@ import com.duckduckgo.app.global.model.SiteFactory import com.duckduckgo.app.global.model.domain import com.duckduckgo.app.global.model.domainMatchesUrl import com.duckduckgo.app.global.plugins.view_model.ViewModelFactoryPlugin -import com.duckduckgo.app.global.useourapp.UseOurAppDetector -import com.duckduckgo.app.global.useourapp.UseOurAppDetector.Companion.USE_OUR_APP_SHORTCUT_TITLE -import com.duckduckgo.app.global.useourapp.UseOurAppDetector.Companion.USE_OUR_APP_SHORTCUT_URL import com.duckduckgo.app.global.view.asLocationPermissionOrigin import com.duckduckgo.app.globalprivacycontrol.GlobalPrivacyControl import com.duckduckgo.app.location.GeoLocationPermissions @@ -88,7 +85,6 @@ import com.duckduckgo.app.location.data.LocationPermissionsRepository import com.duckduckgo.app.location.ui.SiteLocationPermissionDialog import com.duckduckgo.app.location.ui.SystemLocationPermissionDialog import com.duckduckgo.app.notification.db.NotificationDao -import com.duckduckgo.app.notification.model.UseOurAppNotification import com.duckduckgo.app.pixels.AppPixelName import com.duckduckgo.app.privacy.db.NetworkLeaderboardDao import com.duckduckgo.app.privacy.db.UserWhitelistDao @@ -147,7 +143,6 @@ class BrowserTabViewModel( private val dispatchers: DispatcherProvider = DefaultDispatcherProvider(), private val userEventsStore: UserEventsStore, private val notificationDao: NotificationDao, - private val useOurAppDetector: UseOurAppDetector, private val variantManager: VariantManager, private val fileDownloader: FileDownloader, private val globalPrivacyControl: GlobalPrivacyControl, @@ -386,8 +381,6 @@ class BrowserTabViewModel( private val loginDetectionObserver = Observer { loginEvent -> Timber.i("LoginDetection for $loginEvent") viewModelScope.launch(dispatchers.io()) { - useOurAppDetector.registerIfFireproofSeenForTheFirstTime(loginEvent.forwardedToDomain) - if (!isFireproofWebsite(loginEvent.forwardedToDomain)) { withContext(dispatchers.main()) { command.value = AskToFireproofWebsite(FireproofWebsiteEntity(loginEvent.forwardedToDomain)) @@ -426,7 +419,6 @@ class BrowserTabViewModel( fun onViewReady() { url?.let { - sendPixelIfUseOurAppSiteVisitedFirstTime(it) onUserSubmittedQuery(it) } } @@ -801,15 +793,8 @@ class BrowserTabViewModel( private fun pageChanged(url: String, title: String?) { Timber.v("Page changed: $url") - val previousUrl = site?.url - buildSiteFactory(url, title) - // Navigating from different website to use our app website - if (!useOurAppDetector.isUseOurAppUrl(previousUrl)) { - sendPixelIfUseOurAppSiteVisitedFirstTime(url) - } - val currentOmnibarViewState = currentOmnibarViewState() omnibarViewState.value = currentOmnibarViewState.copy(omnibarText = omnibarTextForUrl(url), shouldMoveCaretToEnd = false) val currentBrowserViewState = currentBrowserViewState() @@ -850,27 +835,6 @@ class BrowserTabViewModel( registerSiteVisit() } - private fun sendPixelIfUseOurAppSiteVisitedFirstTime(url: String) { - if (useOurAppDetector.isUseOurAppUrl(url)) { - viewModelScope.launch { sendUseOurAppSiteVisitedPixel() } - } - } - - private suspend fun sendUseOurAppSiteVisitedPixel() { - withContext(dispatchers.io()) { - val isShortcutAdded = userEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED) - val isUseOurAppNotificationSeen = notificationDao.exists(UseOurAppNotification.ID) - val deleteCtaShown = ctaViewModel.useOurAppDeletionDialogShown() - - when { - deleteCtaShown -> pixel.fire(AppPixelName.UOA_VISITED_AFTER_DELETE_CTA) - isShortcutAdded != null -> pixel.fire(AppPixelName.UOA_VISITED_AFTER_SHORTCUT) - isUseOurAppNotificationSeen -> pixel.fire(AppPixelName.UOA_VISITED_AFTER_NOTIFICATION) - else -> pixel.fire(AppPixelName.UOA_VISITED) - } - } - } - private fun shouldShowDaxIcon(currentUrl: String?, showPrivacyGrade: Boolean): Boolean { val url = currentUrl ?: return false return showPrivacyGrade && duckDuckGoUrlDetector.isDuckDuckGoQueryUrl(url) @@ -1658,23 +1622,14 @@ class BrowserTabViewModel( is HomePanelCta.Survey -> LaunchSurvey(cta.survey) is HomePanelCta.AddWidgetAuto -> LaunchAddWidget is HomePanelCta.AddWidgetInstructions -> LaunchLegacyAddWidget - is UseOurAppCta -> navigateToUrlAndLaunchShortcut(url = USE_OUR_APP_SHORTCUT_URL, title = USE_OUR_APP_SHORTCUT_TITLE) else -> return } } - private fun navigateToUrlAndLaunchShortcut(url: String, title: String): AddHomeShortcut { - onUserSubmittedQuery(url) - return AddHomeShortcut(title, url) - } - fun onUserClickCtaSecondaryButton() { viewModelScope.launch { val cta = currentCtaViewState().cta ?: return@launch ctaViewModel.onUserDismissedCta(cta) - if (cta is UseOurAppCta) { - command.value = ShowKeyboard - } } } @@ -1914,7 +1869,6 @@ class BrowserTabViewModelFactory @Inject constructor( private val dispatchers: DispatcherProvider = DefaultDispatcherProvider(), private val userEventsStore: Provider, private val notificationDao: Provider, - private val useOurAppDetector: Provider, private val variantManager: Provider, private val fileDownloader: Provider, private val globalPrivacyControl: Provider, @@ -1924,7 +1878,7 @@ class BrowserTabViewModelFactory @Inject constructor( override fun create(modelClass: Class): T? { with(modelClass) { return when { - isAssignableFrom(BrowserTabViewModel::class.java) -> BrowserTabViewModel(statisticsUpdater.get(), queryUrlConverter.get(), duckDuckGoUrlDetector.get(), siteFactory.get(), tabRepository.get(), userWhitelistDao.get(), networkLeaderboardDao.get(), bookmarksDao.get(), fireproofWebsiteRepository.get(), locationPermissionsRepository.get(), geoLocationPermissions.get(), navigationAwareLoginDetector.get(), autoComplete.get(), appSettingsPreferencesStore.get(), longPressHandler.get(), webViewSessionStorage.get(), specialUrlDetector.get(), faviconManager.get(), addToHomeCapabilityDetector.get(), ctaViewModel.get(), searchCountDao.get(), pixel.get(), dispatchers, userEventsStore.get(), notificationDao.get(), useOurAppDetector.get(), variantManager.get(), fileDownloader.get(), globalPrivacyControl.get(), fireproofDialogsEventHandler.get(), emailManager.get()) as T + isAssignableFrom(BrowserTabViewModel::class.java) -> BrowserTabViewModel(statisticsUpdater.get(), queryUrlConverter.get(), duckDuckGoUrlDetector.get(), siteFactory.get(), tabRepository.get(), userWhitelistDao.get(), networkLeaderboardDao.get(), bookmarksDao.get(), fireproofWebsiteRepository.get(), locationPermissionsRepository.get(), geoLocationPermissions.get(), navigationAwareLoginDetector.get(), autoComplete.get(), appSettingsPreferencesStore.get(), longPressHandler.get(), webViewSessionStorage.get(), specialUrlDetector.get(), faviconManager.get(), addToHomeCapabilityDetector.get(), ctaViewModel.get(), searchCountDao.get(), pixel.get(), dispatchers, userEventsStore.get(), notificationDao.get(), variantManager.get(), fileDownloader.get(), globalPrivacyControl.get(), fireproofDialogsEventHandler.get(), emailManager.get()) as T else -> null } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserViewModel.kt index 047261a1710b..1c5041822c2c 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserViewModel.kt @@ -38,7 +38,6 @@ import com.duckduckgo.app.global.rating.AppEnjoymentPromptEmitter import com.duckduckgo.app.global.rating.AppEnjoymentPromptOptions import com.duckduckgo.app.global.rating.AppEnjoymentUserEventRecorder import com.duckduckgo.app.global.rating.PromptCount -import com.duckduckgo.app.global.useourapp.UseOurAppDetector import com.duckduckgo.app.pixels.AppPixelName import com.duckduckgo.app.privacy.ui.PrivacyDashboardActivity.Companion.RELOAD_RESULT_CODE import com.duckduckgo.app.statistics.pixels.Pixel @@ -61,8 +60,7 @@ class BrowserViewModel( private val appEnjoymentPromptEmitter: AppEnjoymentPromptEmitter, private val appEnjoymentUserEventRecorder: AppEnjoymentUserEventRecorder, private val dispatchers: DispatcherProvider = DefaultDispatcherProvider(), - private val pixel: Pixel, - private val useOurAppDetector: UseOurAppDetector + private val pixel: Pixel ) : AppEnjoymentDialogFragment.Listener, RateAppDialogFragment.Listener, GiveFeedbackDialogFragment.Listener, @@ -230,11 +228,7 @@ class BrowserViewModel( fun onOpenShortcut(url: String) { launch(dispatchers.io()) { tabRepository.selectByUrlOrNewTab(queryUrlConverter.convertQueryToUrl(url)) - if (useOurAppDetector.isUseOurAppUrl(url)) { - pixel.fire(AppPixelName.USE_OUR_APP_SHORTCUT_OPENED) - } else { - pixel.fire(AppPixelName.SHORTCUT_OPENED) - } + pixel.fire(AppPixelName.SHORTCUT_OPENED) } } } @@ -247,13 +241,12 @@ class BrowserViewModelFactory @Inject constructor( val appEnjoymentPromptEmitter: Provider, val appEnjoymentUserEventRecorder: Provider, val dispatchers: DispatcherProvider = DefaultDispatcherProvider(), - val pixel: Provider, - val useOurAppDetector: Provider + val pixel: Provider ) : ViewModelFactoryPlugin { override fun create(modelClass: Class): T? { with(modelClass) { return when { - isAssignableFrom(BrowserViewModel::class.java) -> BrowserViewModel(tabRepository.get(), queryUrlConverter.get(), dataClearer.get(), appEnjoymentPromptEmitter.get(), appEnjoymentUserEventRecorder.get(), dispatchers, pixel.get(), useOurAppDetector.get()) as T + isAssignableFrom(BrowserViewModel::class.java) -> BrowserViewModel(tabRepository.get(), queryUrlConverter.get(), dataClearer.get(), appEnjoymentPromptEmitter.get(), appEnjoymentUserEventRecorder.get(), dispatchers, pixel.get()) as T else -> null } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/di/BrowserModule.kt b/app/src/main/java/com/duckduckgo/app/browser/di/BrowserModule.kt index 477705b43581..e74956059c0f 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/di/BrowserModule.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/di/BrowserModule.kt @@ -55,7 +55,6 @@ import com.duckduckgo.app.global.events.db.UserEventsStore import com.duckduckgo.app.global.exception.UncaughtExceptionRepository import com.duckduckgo.app.global.file.FileDeleter import com.duckduckgo.app.global.install.AppInstallStore -import com.duckduckgo.app.global.useourapp.UseOurAppDetector import com.duckduckgo.app.globalprivacycontrol.GlobalPrivacyControl import com.duckduckgo.app.globalprivacycontrol.GlobalPrivacyControlManager import com.duckduckgo.app.httpsupgrade.HttpsUpgrader @@ -271,8 +270,8 @@ class BrowserModule { } @Provides - fun domLoginDetector(settingsDataStore: SettingsDataStore, useOurAppDetector: UseOurAppDetector): DOMLoginDetector { - return JsLoginDetector(settingsDataStore, useOurAppDetector) + fun domLoginDetector(settingsDataStore: SettingsDataStore): DOMLoginDetector { + return JsLoginDetector(settingsDataStore) } @Provides diff --git a/app/src/main/java/com/duckduckgo/app/browser/logindetection/DOMLoginDetector.kt b/app/src/main/java/com/duckduckgo/app/browser/logindetection/DOMLoginDetector.kt index 138d81baf0be..6d8a543d6414 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/logindetection/DOMLoginDetector.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/logindetection/DOMLoginDetector.kt @@ -23,7 +23,6 @@ import androidx.annotation.UiThread import com.duckduckgo.app.browser.R import com.duckduckgo.app.browser.logindetection.LoginDetectionJavascriptInterface.Companion.JAVASCRIPT_INTERFACE_NAME import com.duckduckgo.app.global.getValidUrl -import com.duckduckgo.app.global.useourapp.UseOurAppDetector import com.duckduckgo.app.settings.db.SettingsDataStore import timber.log.Timber import javax.inject.Inject @@ -38,7 +37,7 @@ sealed class WebNavigationEvent { data class ShouldInterceptRequest(val webView: WebView, val request: WebResourceRequest) : WebNavigationEvent() } -class JsLoginDetector @Inject constructor(private val settingsDataStore: SettingsDataStore, private val useOurAppDetector: UseOurAppDetector) : +class JsLoginDetector @Inject constructor(private val settingsDataStore: SettingsDataStore) : DOMLoginDetector { private val javaScriptDetector = JavaScriptDetector() private val loginPathRegex = Regex("login|sign-in|signin|session") @@ -49,7 +48,7 @@ class JsLoginDetector @Inject constructor(private val settingsDataStore: Setting @UiThread override fun onEvent(event: WebNavigationEvent) { - if (settingsDataStore.appLoginDetection || useOurAppDetector.allowLoginDetection(event)) { + if (settingsDataStore.appLoginDetection) { when (event) { is WebNavigationEvent.OnPageStarted -> injectLoginFormDetectionJS(event.webView) is WebNavigationEvent.ShouldInterceptRequest -> { diff --git a/app/src/main/java/com/duckduckgo/app/browser/shortcut/ShortcutBuilder.kt b/app/src/main/java/com/duckduckgo/app/browser/shortcut/ShortcutBuilder.kt index cff139b23c7c..4bd742392d96 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/shortcut/ShortcutBuilder.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/shortcut/ShortcutBuilder.kt @@ -26,11 +26,10 @@ import androidx.core.graphics.drawable.IconCompat import com.duckduckgo.app.browser.BrowserActivity import com.duckduckgo.app.browser.BrowserTabViewModel import com.duckduckgo.app.browser.R -import com.duckduckgo.app.global.useourapp.UseOurAppDetector import java.util.UUID import javax.inject.Inject -class ShortcutBuilder @Inject constructor(private val useOurAppDetector: UseOurAppDetector) { +class ShortcutBuilder @Inject constructor() { private fun buildPinnedPageShortcut(context: Context, homeShortcut: BrowserTabViewModel.Command.AddHomeShortcut): ShortcutInfoCompat { val intent = Intent(context, BrowserActivity::class.java) @@ -39,7 +38,6 @@ class ShortcutBuilder @Inject constructor(private val useOurAppDetector: UseOurA intent.putExtra(SHORTCUT_EXTRA_ARG, true) val icon = when { - useOurAppDetector.isUseOurAppUrl(homeShortcut.url) -> IconCompat.createWithResource(context, R.drawable.ic_fb_favicon) homeShortcut.icon != null -> IconCompat.createWithBitmap(homeShortcut.icon) else -> IconCompat.createWithResource(context, R.drawable.logo_mini) } @@ -52,10 +50,10 @@ class ShortcutBuilder @Inject constructor(private val useOurAppDetector: UseOurA } private fun buildPendingIntent(context: Context, url: String, title: String): PendingIntent? { - val pinnedShortcutCallbackIntent = Intent(USE_OUR_APP_SHORTCUT_ADDED_ACTION) + val pinnedShortcutCallbackIntent = Intent(SHORTCUT_ADDED_ACTION) pinnedShortcutCallbackIntent.putExtra(SHORTCUT_URL_ARG, url) pinnedShortcutCallbackIntent.putExtra(SHORTCUT_TITLE_ARG, title) - return PendingIntent.getBroadcast(context, USE_OUR_APP_SHORTCUT_ADDED_CODE, pinnedShortcutCallbackIntent, FLAG_UPDATE_CURRENT) + return PendingIntent.getBroadcast(context, SHORTCUT_ADDED_CODE, pinnedShortcutCallbackIntent, FLAG_UPDATE_CURRENT) } fun requestPinShortcut(context: Context, homeShortcut: BrowserTabViewModel.Command.AddHomeShortcut) { @@ -66,8 +64,8 @@ class ShortcutBuilder @Inject constructor(private val useOurAppDetector: UseOurA } companion object { - const val USE_OUR_APP_SHORTCUT_ADDED_ACTION: String = "useOurAppShortcutAdded" - const val USE_OUR_APP_SHORTCUT_ADDED_CODE = 9000 + const val SHORTCUT_ADDED_ACTION: String = "appShortcutAdded" + const val SHORTCUT_ADDED_CODE = 9000 const val SHORTCUT_EXTRA_ARG = "shortCutAdded" const val SHORTCUT_URL_ARG = "shortcutUrl" diff --git a/app/src/main/java/com/duckduckgo/app/browser/shortcut/ShortcutReceiver.kt b/app/src/main/java/com/duckduckgo/app/browser/shortcut/ShortcutReceiver.kt index f5acdc705cbc..bdddf330e699 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/shortcut/ShortcutReceiver.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/shortcut/ShortcutReceiver.kt @@ -23,46 +23,30 @@ import android.os.Build import android.widget.Toast import com.duckduckgo.app.browser.R import com.duckduckgo.app.browser.shortcut.ShortcutBuilder.Companion.SHORTCUT_TITLE_ARG -import com.duckduckgo.app.browser.shortcut.ShortcutBuilder.Companion.SHORTCUT_URL_ARG import com.duckduckgo.app.global.DispatcherProvider -import com.duckduckgo.app.global.events.db.UserEventKey -import com.duckduckgo.app.global.events.db.UserEventsStore -import com.duckduckgo.app.global.useourapp.UseOurAppDetector import com.duckduckgo.app.pixels.AppPixelName -import com.duckduckgo.app.statistics.VariantManager import com.duckduckgo.app.statistics.pixels.Pixel import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import javax.inject.Inject class ShortcutReceiver @Inject constructor( - private val useOurAppDetector: UseOurAppDetector, private val pixel: Pixel, - private val userEventsStore: UserEventsStore, - private val dispatcher: DispatcherProvider, - private val variantManager: VariantManager + private val dispatcher: DispatcherProvider ) : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { - val originUrl = intent?.getStringExtra(SHORTCUT_URL_ARG) val title = intent?.getStringExtra(SHORTCUT_TITLE_ARG) if (!IGNORE_MANUFACTURERS_LIST.contains(Build.MANUFACTURER)) { context?.let { - Toast.makeText(it, it.getString(R.string.useOurAppShortcutAddedText, title), Toast.LENGTH_SHORT).show() + Toast.makeText(it, it.getString(R.string.shortcutAddedText, title), Toast.LENGTH_SHORT).show() } } GlobalScope.launch(dispatcher.io()) { - if (useOurAppDetector.isUseOurAppUrl(originUrl)) { - pixel.fire(AppPixelName.USE_OUR_APP_SHORTCUT_ADDED) - if (variantManager.getVariant().hasFeature(VariantManager.VariantFeature.InAppUsage)) { - userEventsStore.registerUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED) - } - } else { - pixel.fire(AppPixelName.SHORTCUT_ADDED) - } + pixel.fire(AppPixelName.SHORTCUT_ADDED) } } diff --git a/app/src/main/java/com/duckduckgo/app/cta/model/DismissedCta.kt b/app/src/main/java/com/duckduckgo/app/cta/model/DismissedCta.kt index 09a37b1c4b6f..697f5ae94d5c 100644 --- a/app/src/main/java/com/duckduckgo/app/cta/model/DismissedCta.kt +++ b/app/src/main/java/com/duckduckgo/app/cta/model/DismissedCta.kt @@ -30,8 +30,6 @@ enum class CtaId { DAX_DIALOG_NETWORK, DAX_DIALOG_OTHER, DAX_END, - USE_OUR_APP, - USE_OUR_APP_DELETION, DAX_FIRE_BUTTON_PULSE, UNKNOWN } diff --git a/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt b/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt index 015065d93b45..e094f65d0e90 100644 --- a/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt +++ b/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt @@ -68,56 +68,6 @@ interface Cta { fun pixelOkParameters(): Map } -class UseOurAppCta( - @StringRes val text: Int = R.string.useOurAppDialogText, - @StringRes val okButton: Int = R.string.useOurAppDialogButtonText, - @StringRes val cancelButton: Int = R.string.useOurAppDialogCancelButtonText, - override val ctaId: CtaId = CtaId.USE_OUR_APP, - override val shownPixel: Pixel.PixelName? = null, - override val okPixel: Pixel.PixelName? = AppPixelName.USE_OUR_APP_DIALOG_OK, - override val cancelPixel: Pixel.PixelName? = null -) : Cta, DialogCta { - - override fun createCta(activity: FragmentActivity): DaxDialog = - TypewriterDaxDialog.newInstance( - daxText = activity.resources.getString(text), - primaryButtonText = activity.resources.getString(okButton), - secondaryButtonText = activity.resources.getString(cancelButton), - dismissible = false, - showHideButton = false - ) - - override fun pixelCancelParameters(): Map = emptyMap() - - override fun pixelOkParameters(): Map = emptyMap() - - override fun pixelShownParameters(): Map = emptyMap() -} - -class UseOurAppDeletionCta( - @StringRes val text: Int = R.string.useOurAppDeletionDialogText, - @StringRes val okButton: Int = R.string.daxDialogGotIt, - override val ctaId: CtaId = CtaId.USE_OUR_APP_DELETION, - override val shownPixel: Pixel.PixelName? = AppPixelName.USE_OUR_APP_DIALOG_DELETE_SHOWN, - override val okPixel: Pixel.PixelName? = null, - override val cancelPixel: Pixel.PixelName? = null -) : Cta, DialogCta { - - override fun createCta(activity: FragmentActivity): DaxDialog = - TypewriterDaxDialog.newInstance( - daxText = activity.resources.getString(text), - primaryButtonText = activity.resources.getString(okButton), - dismissible = false, - showHideButton = false - ) - - override fun pixelCancelParameters(): Map = emptyMap() - - override fun pixelOkParameters(): Map = emptyMap() - - override fun pixelShownParameters(): Map = emptyMap() -} - sealed class DaxDialogCta( override val ctaId: CtaId, @AnyRes open val description: Int, diff --git a/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt b/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt index 3031533900e7..4c2b2ce3b9e7 100644 --- a/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt @@ -24,19 +24,15 @@ import com.duckduckgo.app.cta.model.CtaId import com.duckduckgo.app.cta.model.DismissedCta import com.duckduckgo.app.cta.ui.HomePanelCta.* import com.duckduckgo.app.global.DispatcherProvider -import com.duckduckgo.app.global.events.db.UserEventKey -import com.duckduckgo.app.global.events.db.UserEventsStore import com.duckduckgo.app.global.install.AppInstallStore import com.duckduckgo.app.global.install.daysInstalled import com.duckduckgo.app.global.model.Site import com.duckduckgo.app.global.model.domain import com.duckduckgo.app.global.model.orderedTrackingEntities -import com.duckduckgo.app.global.useourapp.UseOurAppDetector import com.duckduckgo.app.onboarding.store.* import com.duckduckgo.app.pixels.AppPixelName import com.duckduckgo.app.privacy.db.UserWhitelistDao import com.duckduckgo.app.settings.db.SettingsDataStore -import com.duckduckgo.app.statistics.VariantManager import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.survey.db.SurveyDao import com.duckduckgo.app.survey.model.Survey @@ -49,7 +45,6 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.withContext import timber.log.Timber import java.util.* -import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton import kotlin.coroutines.CoroutineContext @@ -62,12 +57,9 @@ class CtaViewModel @Inject constructor( private val widgetCapabilities: WidgetCapabilities, private val dismissedCtaDao: DismissedCtaDao, private val userWhitelistDao: UserWhitelistDao, - private val variantManager: VariantManager, private val settingsDataStore: SettingsDataStore, private val onboardingStore: OnboardingStore, private val userStageStore: UserStageStore, - private val userEventsStore: UserEventsStore, - private val useOurAppDetector: UseOurAppDetector, private val tabRepository: TabRepository, private val dispatchers: DispatcherProvider ) { @@ -148,13 +140,6 @@ class CtaViewModel @Inject constructor( } } - private suspend fun completeStageIfUserInUseOurAppCompleted() { - if (useOurAppActive()) { - Timber.d("Completing USE OUR APP ONBOARDING") - userStageStore.stageCompleted(AppStage.USE_OUR_APP_ONBOARDING) - } - } - suspend fun onUserDismissedCta(cta: Cta) { withContext(dispatchers.io()) { cta.cancelPixel?.let { @@ -168,7 +153,6 @@ class CtaViewModel @Inject constructor( dismissedCtaDao.insert(DismissedCta(cta.ctaId)) } - completeStageIfUserInUseOurAppCompleted() completeStageIfDaxOnboardingCompleted() } } @@ -210,9 +194,6 @@ class CtaViewModel @Inject constructor( canShowDaxCtaEndOfJourney() -> { DaxBubbleCta.DaxEndCta(onboardingStore, appInstallStore) } - canShowUseOurAppDialog() -> { - UseOurAppCta() - } canShowWidgetCta() -> { if (widgetCapabilities.supportsAutomaticWidgetAdd) AddWidgetAuto else AddWidgetInstructions } @@ -225,9 +206,6 @@ class CtaViewModel @Inject constructor( canShowDaxDialogCta() -> { getDaxDialogCta(site) } - canShowUseOurAppDeletionDialog(site) -> { - UseOurAppDeletionCta() - } else -> null } } @@ -251,20 +229,6 @@ class CtaViewModel @Inject constructor( return null } - @WorkerThread - private suspend fun canShowUseOurAppDeletionDialog(site: Site?): Boolean = - !settingsDataStore.hideTips && !useOurAppDeletionDialogShown() && useOurAppDetector.isUseOurAppUrl(site?.url) && twoDaysSinceShortcutAdded() - - @WorkerThread - private suspend fun twoDaysSinceShortcutAdded(): Boolean { - val timestampKey = userEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED) ?: return false - val days = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - timestampKey.timestamp) - return (days >= 2) - } - - @WorkerThread - private suspend fun canShowUseOurAppDialog(): Boolean = !settingsDataStore.hideTips && useOurAppActive() && !useOurAppDialogShown() - @WorkerThread private fun canShowWidgetCta(): Boolean { return widgetCapabilities.supportsStandardWidgetAdd && @@ -323,10 +287,6 @@ class CtaViewModel @Inject constructor( } } - fun useOurAppDeletionDialogShown(): Boolean = dismissedCtaDao.exists(CtaId.USE_OUR_APP_DELETION) - - private fun useOurAppDialogShown(): Boolean = dismissedCtaDao.exists(CtaId.USE_OUR_APP) - private fun daxDialogIntroShown(): Boolean = dismissedCtaDao.exists(CtaId.DAX_INTRO) private fun daxDialogEndShown(): Boolean = dismissedCtaDao.exists(CtaId.DAX_END) @@ -345,8 +305,6 @@ class CtaViewModel @Inject constructor( private fun isSerpUrl(url: String): Boolean = url.contains(DaxDialogCta.SERP) - private suspend fun useOurAppActive(): Boolean = userStageStore.useOurAppOnboarding() - private suspend fun daxOnboardingActive(): Boolean = userStageStore.daxOnboardingActive() private suspend fun pulseAnimationDisabled(): Boolean = !daxOnboardingActive() || pulseFireButtonShown() || daxDialogFireEducationShown() || settingsDataStore.hideTips diff --git a/app/src/main/java/com/duckduckgo/app/di/DatabaseModule.kt b/app/src/main/java/com/duckduckgo/app/di/DatabaseModule.kt index ad94a1254b60..5535917152d3 100644 --- a/app/src/main/java/com/duckduckgo/app/di/DatabaseModule.kt +++ b/app/src/main/java/com/duckduckgo/app/di/DatabaseModule.kt @@ -20,10 +20,8 @@ import android.content.Context import android.webkit.WebViewDatabase import androidx.room.Room import androidx.room.RoomDatabase -import com.duckduckgo.app.browser.addtohome.AddToHomeCapabilityDetector import com.duckduckgo.app.global.db.AppDatabase import com.duckduckgo.app.global.db.MigrationsProvider -import com.duckduckgo.app.settings.db.SettingsDataStore import dagger.Module import dagger.Provides import javax.inject.Singleton @@ -49,11 +47,7 @@ class DatabaseModule { } @Provides - fun provideDatabaseMigrations( - context: Context, - settingsDataStore: SettingsDataStore, - addToHomeCapabilityDetector: AddToHomeCapabilityDetector - ): MigrationsProvider { - return MigrationsProvider(context, settingsDataStore, addToHomeCapabilityDetector) + fun provideDatabaseMigrations(context: Context): MigrationsProvider { + return MigrationsProvider(context) } } diff --git a/app/src/main/java/com/duckduckgo/app/di/NotificationModule.kt b/app/src/main/java/com/duckduckgo/app/di/NotificationModule.kt index a41256b425f9..11ad9ceffc82 100644 --- a/app/src/main/java/com/duckduckgo/app/di/NotificationModule.kt +++ b/app/src/main/java/com/duckduckgo/app/di/NotificationModule.kt @@ -21,18 +21,14 @@ import android.content.Context import androidx.core.app.NotificationManagerCompat import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.work.WorkManager -import com.duckduckgo.app.browser.addtohome.AddToHomeCapabilityDetector -import com.duckduckgo.app.global.install.AppInstallStore import com.duckduckgo.app.notification.AndroidNotificationScheduler import com.duckduckgo.app.notification.NotificationFactory import com.duckduckgo.app.notification.NotificationScheduler import com.duckduckgo.app.notification.db.NotificationDao import com.duckduckgo.app.notification.model.ClearDataNotification -import com.duckduckgo.app.notification.model.UseOurAppNotification import com.duckduckgo.app.notification.model.PrivacyProtectionNotification import com.duckduckgo.app.privacy.db.PrivacyProtectionCountDao import com.duckduckgo.app.settings.db.SettingsDataStore -import com.duckduckgo.app.statistics.VariantManager import dagger.Module import dagger.Provides import javax.inject.Singleton @@ -58,16 +54,6 @@ class NotificationModule { return LocalBroadcastManager.getInstance(context) } - @Provides - fun provideUseOurAppNotification( - context: Context, - notificationDao: NotificationDao, - settingsDataStore: SettingsDataStore, - addToHomeCapabilityDetector: AddToHomeCapabilityDetector - ): UseOurAppNotification { - return UseOurAppNotification(context, notificationDao, settingsDataStore, addToHomeCapabilityDetector) - } - @Provides fun provideClearDataNotification( context: Context, @@ -91,18 +77,12 @@ class NotificationModule { fun providesNotificationScheduler( workManager: WorkManager, clearDataNotification: ClearDataNotification, - privacyProtectionNotification: PrivacyProtectionNotification, - useOurAppNotification: UseOurAppNotification, - variantManager: VariantManager, - appInstallStore: AppInstallStore + privacyProtectionNotification: PrivacyProtectionNotification ): AndroidNotificationScheduler { return NotificationScheduler( workManager, clearDataNotification, - privacyProtectionNotification, - useOurAppNotification, - variantManager, - appInstallStore + privacyProtectionNotification ) } diff --git a/app/src/main/java/com/duckduckgo/app/di/VariantModule.kt b/app/src/main/java/com/duckduckgo/app/di/VariantModule.kt index fc5d0a5b2f7e..501c3d813b1b 100644 --- a/app/src/main/java/com/duckduckgo/app/di/VariantModule.kt +++ b/app/src/main/java/com/duckduckgo/app/di/VariantModule.kt @@ -16,7 +16,6 @@ package com.duckduckgo.app.di -import com.duckduckgo.app.global.useourapp.UseOurAppMigrationManager import com.duckduckgo.app.statistics.ExperimentationVariantManager import com.duckduckgo.app.statistics.VariantManager import com.duckduckgo.app.statistics.WeightedRandomizer @@ -36,9 +35,4 @@ class VariantModule { @Provides fun weightedRandomizer() = WeightedRandomizer() - @Provides - @Singleton - fun useOurAppMigrationManager(weightedRandomizer: WeightedRandomizer): UseOurAppMigrationManager { - return UseOurAppMigrationManager(weightedRandomizer) - } } diff --git a/app/src/main/java/com/duckduckgo/app/global/DuckDuckGoApplication.kt b/app/src/main/java/com/duckduckgo/app/global/DuckDuckGoApplication.kt index 1099ab354ac4..b8d5fddd86c4 100644 --- a/app/src/main/java/com/duckduckgo/app/global/DuckDuckGoApplication.kt +++ b/app/src/main/java/com/duckduckgo/app/global/DuckDuckGoApplication.kt @@ -137,7 +137,7 @@ open class DuckDuckGoApplication : HasAndroidInjector, Application(), LifecycleO scheduleOfflinePixels() notificationRegistrar.registerApp() - registerReceiver(shortcutReceiver, IntentFilter(ShortcutBuilder.USE_OUR_APP_SHORTCUT_ADDED_ACTION)) + registerReceiver(shortcutReceiver, IntentFilter(ShortcutBuilder.SHORTCUT_ADDED_ACTION)) initializeHttpsUpgrader() submitUnsentFirePixels() diff --git a/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt b/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt index cec7501d3ff8..b89bf61e6329 100644 --- a/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt +++ b/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt @@ -24,7 +24,6 @@ import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase import com.duckduckgo.app.bookmarks.db.BookmarkEntity import com.duckduckgo.app.bookmarks.db.BookmarksDao -import com.duckduckgo.app.browser.addtohome.AddToHomeCapabilityDetector import com.duckduckgo.app.browser.cookies.db.AuthCookiesAllowedDomainsDao import com.duckduckgo.app.browser.cookies.db.AuthCookieAllowedDomainEntity import com.duckduckgo.app.browser.rating.db.* @@ -50,7 +49,6 @@ import com.duckduckgo.app.onboarding.store.* import com.duckduckgo.app.privacy.db.* import com.duckduckgo.app.privacy.model.PrivacyProtectionCountsEntity import com.duckduckgo.app.privacy.model.UserWhitelistedDomain -import com.duckduckgo.app.settings.db.SettingsDataStore import com.duckduckgo.app.statistics.model.PixelEntity import com.duckduckgo.app.statistics.model.QueryParamsTypeConverter import com.duckduckgo.app.statistics.store.PendingPixelDao @@ -67,7 +65,7 @@ import com.duckduckgo.app.usage.search.SearchCountDao import com.duckduckgo.app.usage.search.SearchCountEntity @Database( - exportSchema = true, version = 33, + exportSchema = true, version = 34, entities = [ TdsTracker::class, TdsEntity::class, @@ -143,11 +141,7 @@ abstract class AppDatabase : RoomDatabase() { } @Suppress("PropertyName") -class MigrationsProvider( - val context: Context, - val settingsDataStore: SettingsDataStore, - val addToHomeCapabilityDetector: AddToHomeCapabilityDetector -) { +class MigrationsProvider(val context: Context) { val MIGRATION_1_TO_2: Migration = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { @@ -324,7 +318,7 @@ class MigrationsProvider( val MIGRATION_22_TO_23: Migration = object : Migration(22, 23) { override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("UPDATE $USER_STAGE_TABLE_NAME SET appStage = \"${AppStage.ESTABLISHED}\" WHERE appStage = \"${AppStage.USE_OUR_APP_NOTIFICATION}\"") + database.execSQL("UPDATE $USER_STAGE_TABLE_NAME SET appStage = \"${AppStage.ESTABLISHED}\" WHERE appStage = \"AppStage.USE_OUR_APP_NOTIFICATION\"") } } @@ -414,6 +408,13 @@ class MigrationsProvider( } } + val MIGRATION_33_TO_34: Migration = object : Migration(33, 34) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("UPDATE $USER_STAGE_TABLE_NAME SET appStage = \"${AppStage.ESTABLISHED}\" WHERE appStage = \"USE_OUR_APP_NOTIFICATION\" OR appStage = \"USE_OUR_APP_ONBOARDING\"") + database.execSQL("DELETE FROM user_events WHERE id = \"USE_OUR_APP_SHORTCUT_ADDED\" OR id = \"USE_OUR_APP_FIREPROOF_DIALOG_SEEN\"") + } + } + val BOOKMARKS_DB_ON_CREATE = object : RoomDatabase.Callback() { override fun onCreate(database: SupportSQLiteDatabase) { MIGRATION_29_TO_30.migrate(database) @@ -459,7 +460,8 @@ class MigrationsProvider( MIGRATION_29_TO_30, MIGRATION_30_TO_31, MIGRATION_31_TO_32, - MIGRATION_32_TO_33 + MIGRATION_32_TO_33, + MIGRATION_33_TO_34 ) @Deprecated( diff --git a/app/src/main/java/com/duckduckgo/app/global/events/db/UserEventEntity.kt b/app/src/main/java/com/duckduckgo/app/global/events/db/UserEventEntity.kt index fb7cf65f87b9..a8c78040bc65 100644 --- a/app/src/main/java/com/duckduckgo/app/global/events/db/UserEventEntity.kt +++ b/app/src/main/java/com/duckduckgo/app/global/events/db/UserEventEntity.kt @@ -27,8 +27,6 @@ data class UserEventEntity( ) enum class UserEventKey { - USE_OUR_APP_SHORTCUT_ADDED, - USE_OUR_APP_FIREPROOF_DIALOG_SEEN, FIRE_BUTTON_HIGHLIGHTED, FIRE_BUTTON_EXECUTED, FIREPROOF_LOGIN_DIALOG_DISMISSED, diff --git a/app/src/main/java/com/duckduckgo/app/global/useourapp/UseOurAppDetector.kt b/app/src/main/java/com/duckduckgo/app/global/useourapp/UseOurAppDetector.kt deleted file mode 100644 index 34e41682dafc..000000000000 --- a/app/src/main/java/com/duckduckgo/app/global/useourapp/UseOurAppDetector.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2020 DuckDuckGo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.duckduckgo.app.global.useourapp - -import android.net.Uri -import androidx.core.net.toUri -import com.duckduckgo.app.browser.logindetection.WebNavigationEvent -import com.duckduckgo.app.global.baseHost -import com.duckduckgo.app.global.events.db.UserEventKey -import com.duckduckgo.app.global.events.db.UserEventsStore -import kotlinx.coroutines.runBlocking -import javax.inject.Inject - -class UseOurAppDetector @Inject constructor(val userEventsStore: UserEventsStore) { - - fun isUseOurAppUrl(url: String?): Boolean { - if (url == null) return false - return isUseOurAppUrl(url.toUri()) - } - - fun allowLoginDetection(event: WebNavigationEvent): Boolean { - val canShowFireproof = runBlocking { - if (userEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_SHORTCUT_ADDED) == null) { - false - } else { - (userEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN) == null) - } - } - - return if (canShowFireproof) { - when (event) { - is WebNavigationEvent.OnPageStarted -> isUseOurAppUrl(event.webView.url) - is WebNavigationEvent.ShouldInterceptRequest -> isUseOurAppUrl(event.webView.url) - } - } else { - false - } - } - - suspend fun registerIfFireproofSeenForTheFirstTime(url: String) { - if (userEventsStore.getUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN) == null && isUseOurAppUrl(url)) { - userEventsStore.registerUserEvent(UserEventKey.USE_OUR_APP_FIREPROOF_DIALOG_SEEN) - } - } - - private fun isUseOurAppUrl(uri: Uri): Boolean { - return domainMatchesUrl(uri) - } - - private fun domainMatchesUrl(uri: Uri): Boolean { - return uri.baseHost?.contains(USE_OUR_APP_DOMAIN) ?: false - } - - companion object { - const val USE_OUR_APP_SHORTCUT_URL: String = "https://m.facebook.com/" - const val USE_OUR_APP_SHORTCUT_TITLE: String = "Facebook" - const val USE_OUR_APP_DOMAIN = "facebook.com" - const val USE_OUR_APP_DOMAIN_QUERY = "%facebook.com%" - } -} diff --git a/app/src/main/java/com/duckduckgo/app/global/useourapp/UseOurAppMigrationManager.kt b/app/src/main/java/com/duckduckgo/app/global/useourapp/UseOurAppMigrationManager.kt deleted file mode 100644 index 7a8628a199b9..000000000000 --- a/app/src/main/java/com/duckduckgo/app/global/useourapp/UseOurAppMigrationManager.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020 DuckDuckGo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.duckduckgo.app.global.useourapp - -import com.duckduckgo.app.statistics.IndexRandomizer -import com.duckduckgo.app.statistics.Probabilistic - -interface MigrationManager { - fun shouldRunMigration(): Boolean -} - -class UseOurAppMigrationManager constructor(private val indexRandomizer: IndexRandomizer) : MigrationManager { - - override fun shouldRunMigration(): Boolean { - val randomizedIndex = indexRandomizer.random(MIGRATION_VARIANTS) - val variant = MIGRATION_VARIANTS[randomizedIndex] - return (variant == useOurAppMigration) - } - - companion object { - private val defaultMigration = MigrationWeight(0.9) // do not migrate 90% of old users - private val useOurAppMigration = MigrationWeight(0.1) // migrate 10% of old users - - val MIGRATION_VARIANTS = listOf( - defaultMigration, - useOurAppMigration - ) - } -} - -data class MigrationWeight(override val weight: Double) : Probabilistic diff --git a/app/src/main/java/com/duckduckgo/app/job/JobCleaner.kt b/app/src/main/java/com/duckduckgo/app/job/JobCleaner.kt index 7597fc9547c8..7b317773d7a5 100644 --- a/app/src/main/java/com/duckduckgo/app/job/JobCleaner.kt +++ b/app/src/main/java/com/duckduckgo/app/job/JobCleaner.kt @@ -24,8 +24,9 @@ interface JobCleaner { companion object { private const val STICKY_SEARCH_CONTINUOUS_APP_USE_REQUEST_TAG = "com.duckduckgo.notification.schedule.continuous" + private const val USE_OUR_APP_WORK_REQUEST_TAG = "com.duckduckgo.notification.useOurApp" - fun allDeprecatedNotificationWorkTags() = listOf(STICKY_SEARCH_CONTINUOUS_APP_USE_REQUEST_TAG) + fun allDeprecatedNotificationWorkTags() = listOf(STICKY_SEARCH_CONTINUOUS_APP_USE_REQUEST_TAG, USE_OUR_APP_WORK_REQUEST_TAG) } } diff --git a/app/src/main/java/com/duckduckgo/app/notification/AndroidNotificationScheduler.kt b/app/src/main/java/com/duckduckgo/app/notification/AndroidNotificationScheduler.kt index e46169a33662..cb9482254fc5 100644 --- a/app/src/main/java/com/duckduckgo/app/notification/AndroidNotificationScheduler.kt +++ b/app/src/main/java/com/duckduckgo/app/notification/AndroidNotificationScheduler.kt @@ -17,24 +17,19 @@ package com.duckduckgo.app.notification import android.content.Context -import androidx.annotation.VisibleForTesting import androidx.annotation.WorkerThread import androidx.core.app.NotificationManagerCompat import androidx.work.* -import com.duckduckgo.app.global.install.AppInstallStore -import com.duckduckgo.app.global.install.daysInstalled import com.duckduckgo.app.global.plugins.worker.WorkerInjectorPlugin import com.duckduckgo.app.notification.db.NotificationDao import com.duckduckgo.app.notification.model.ClearDataNotification import com.duckduckgo.app.notification.model.Notification import com.duckduckgo.app.notification.model.PrivacyProtectionNotification import com.duckduckgo.app.notification.model.SchedulableNotification -import com.duckduckgo.app.notification.model.UseOurAppNotification import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.pixels.AppPixelName.NOTIFICATION_SHOWN import com.duckduckgo.di.scopes.AppObjectGraph import com.squareup.anvil.annotations.ContributesMultibinding -import com.duckduckgo.app.statistics.VariantManager import timber.log.Timber import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -49,73 +44,27 @@ interface AndroidNotificationScheduler { class NotificationScheduler( private val workManager: WorkManager, private val clearDataNotification: SchedulableNotification, - private val privacyNotification: SchedulableNotification, - private val useOurAppNotification: SchedulableNotification, - private val variantManager: VariantManager, - private val appInstallStore: AppInstallStore + private val privacyNotification: SchedulableNotification ) : AndroidNotificationScheduler { override suspend fun scheduleNextNotification() { - scheduleUseOurAppNotification() scheduleInactiveUserNotifications() } - private suspend fun scheduleUseOurAppNotification() { - if (variant().hasFeature(VariantManager.VariantFeature.InAppUsage) && useOurAppNotification.canShow()) { - val operation = scheduleUniqueNotification( - OneTimeWorkRequestBuilder(), - UOA_DELAY_DURATION_IN_DAYS, - TimeUnit.DAYS, - USE_OUR_APP_WORK_REQUEST_TAG - ) - try { - operation.await() - } catch (e: Exception) { - Timber.v("Notification could not be scheduled: $e") - } - } - } - private suspend fun scheduleInactiveUserNotifications() { workManager.cancelAllWorkByTag(UNUSED_APP_WORK_REQUEST_TAG) when { - (!variant().hasFeature(VariantManager.VariantFeature.RemoveDay1AndDay3Notifications) && privacyNotification.canShow()) -> { - val duration = getDurationForInactiveNotification(PRIVACY_DELAY_DURATION_IN_DAYS) - scheduleNotification(OneTimeWorkRequestBuilder(), duration, TimeUnit.DAYS, UNUSED_APP_WORK_REQUEST_TAG) + privacyNotification.canShow() -> { + scheduleNotification(OneTimeWorkRequestBuilder(), PRIVACY_DELAY_DURATION_IN_DAYS, TimeUnit.DAYS, UNUSED_APP_WORK_REQUEST_TAG) } - (!variant().hasFeature(VariantManager.VariantFeature.RemoveDay1AndDay3Notifications) && clearDataNotification.canShow()) -> { - val duration = getDurationForInactiveNotification(CLEAR_DATA_DELAY_DURATION_IN_DAYS) - scheduleNotification(OneTimeWorkRequestBuilder(), duration, TimeUnit.DAYS, UNUSED_APP_WORK_REQUEST_TAG) + clearDataNotification.canShow() -> { + scheduleNotification(OneTimeWorkRequestBuilder(), CLEAR_DATA_DELAY_DURATION_IN_DAYS, TimeUnit.DAYS, UNUSED_APP_WORK_REQUEST_TAG) } else -> Timber.v("Notifications not enabled for this variant") } } - @VisibleForTesting - fun getDurationForInactiveNotification(day: Long): Long { - Timber.d("Inactive notification days installed is ${appInstallStore.daysInstalled()} day is $day") - var duration = day - if (variantHasInAppUsage() && (appInstallStore.daysInstalled() + day) == UOA_DELAY_DURATION_IN_DAYS) { - duration += 1 - } - return duration - } - - private fun variantHasInAppUsage() = variant().hasFeature(VariantManager.VariantFeature.InAppUsage) - - private fun variant() = variantManager.getVariant() - - private fun scheduleUniqueNotification(builder: OneTimeWorkRequest.Builder, duration: Long, unit: TimeUnit, tag: String): Operation { - Timber.v("Scheduling unique notification") - val request = builder - .addTag(tag) - .setInitialDelay(duration, unit) - .build() - - return workManager.enqueueUniqueWork(tag, ExistingWorkPolicy.KEEP, request) - } - private fun scheduleNotification(builder: OneTimeWorkRequest.Builder, duration: Long, unit: TimeUnit, tag: String) { Timber.v("Scheduling notification for $duration") val request = builder @@ -132,7 +81,6 @@ class NotificationScheduler( open class ClearDataNotificationWorker(context: Context, params: WorkerParameters) : SchedulableNotificationWorker(context, params) class PrivacyNotificationWorker(context: Context, params: WorkerParameters) : SchedulableNotificationWorker(context, params) - class UseOurAppNotificationWorker(context: Context, params: WorkerParameters) : SchedulableNotificationWorker(context, params) open class SchedulableNotificationWorker(val context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { @@ -163,8 +111,6 @@ class NotificationScheduler( companion object { const val UNUSED_APP_WORK_REQUEST_TAG = "com.duckduckgo.notification.schedule" - const val USE_OUR_APP_WORK_REQUEST_TAG = "com.duckduckgo.notification.useOurApp" - const val UOA_DELAY_DURATION_IN_DAYS = 3L const val CLEAR_DATA_DELAY_DURATION_IN_DAYS = 3L const val PRIVACY_DELAY_DURATION_IN_DAYS = 1L } @@ -213,24 +159,3 @@ class PrivacyNotificationWorkerInjectorPlugin @Inject constructor( return false } } -@ContributesMultibinding(AppObjectGraph::class) -class UseOurAppNotificationWorkerInjectorPlugin @Inject constructor( - private val notificationManagerCompat: NotificationManagerCompat, - private val notificationDao: NotificationDao, - private val notificationFactory: NotificationFactory, - private val pixel: Pixel, - private val useOurAppNotification: UseOurAppNotification -) : WorkerInjectorPlugin { - - override fun inject(worker: ListenableWorker): Boolean { - if (worker is NotificationScheduler.UseOurAppNotificationWorker) { - worker.manager = notificationManagerCompat - worker.notificationDao = notificationDao - worker.factory = notificationFactory - worker.pixel = pixel - worker.notification = useOurAppNotification - return true - } - return false - } -} diff --git a/app/src/main/java/com/duckduckgo/app/notification/NotificationHandlerService.kt b/app/src/main/java/com/duckduckgo/app/notification/NotificationHandlerService.kt index c0128f751fd2..7d91bf793252 100644 --- a/app/src/main/java/com/duckduckgo/app/notification/NotificationHandlerService.kt +++ b/app/src/main/java/com/duckduckgo/app/notification/NotificationHandlerService.kt @@ -30,20 +30,14 @@ import com.duckduckgo.app.notification.NotificationHandlerService.NotificationEv import com.duckduckgo.app.notification.NotificationHandlerService.NotificationEvent.CANCEL import com.duckduckgo.app.notification.NotificationHandlerService.NotificationEvent.CHANGE_ICON_FEATURE import com.duckduckgo.app.notification.NotificationHandlerService.NotificationEvent.CLEAR_DATA_LAUNCH -import com.duckduckgo.app.notification.NotificationHandlerService.NotificationEvent.USE_OUR_APP import com.duckduckgo.app.notification.NotificationHandlerService.NotificationEvent.WEBSITE import com.duckduckgo.app.notification.model.NotificationSpec import com.duckduckgo.app.notification.model.WebsiteNotificationSpecification -import com.duckduckgo.app.onboarding.store.AppStage -import com.duckduckgo.app.onboarding.store.UserStageStore import com.duckduckgo.app.settings.SettingsActivity -import com.duckduckgo.app.settings.db.SettingsDataStore import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.pixels.AppPixelName.NOTIFICATION_CANCELLED import com.duckduckgo.app.pixels.AppPixelName.NOTIFICATION_LAUNCHED import dagger.android.AndroidInjection -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -61,12 +55,6 @@ class NotificationHandlerService : IntentService("NotificationHandlerService") { @Inject lateinit var notificationScheduler: AndroidNotificationScheduler - @Inject - lateinit var settingsDataStore: SettingsDataStore - - @Inject - lateinit var userStageStore: UserStageStore - @Inject lateinit var dispatcher: DispatcherProvider @@ -85,12 +73,6 @@ class NotificationHandlerService : IntentService("NotificationHandlerService") { CANCEL -> onCancelled(pixelSuffix) WEBSITE -> onWebsiteNotification(intent, pixelSuffix) CHANGE_ICON_FEATURE -> onCustomizeIconLaunched(pixelSuffix) - USE_OUR_APP -> { - GlobalScope.launch(dispatcher.io()) { - userStageStore.moveToStage(AppStage.USE_OUR_APP_ONBOARDING) - onAppLaunched(pixelSuffix) - } - } } if (intent.getBooleanExtra(NOTIFICATION_AUTO_CANCEL, true)) { @@ -153,7 +135,6 @@ class NotificationHandlerService : IntentService("NotificationHandlerService") { const val CANCEL = "com.duckduckgo.notification.cancel" const val WEBSITE = "com.duckduckgo.notification.website" const val CHANGE_ICON_FEATURE = "com.duckduckgo.notification.app.feature.changeIcon" - const val USE_OUR_APP = "com.duckduckgo.notification.flow.useOurApp" } companion object { diff --git a/app/src/main/java/com/duckduckgo/app/notification/NotificationRegistrar.kt b/app/src/main/java/com/duckduckgo/app/notification/NotificationRegistrar.kt index 38dc02e9dfb6..610b8dfa8601 100644 --- a/app/src/main/java/com/duckduckgo/app/notification/NotificationRegistrar.kt +++ b/app/src/main/java/com/duckduckgo/app/notification/NotificationRegistrar.kt @@ -51,7 +51,6 @@ class NotificationRegistrar @Inject constructor( const val PrivacyProtection = 101 const val Article = 103 // 102 was used for the search notification hence using 103 moving forward const val AppFeature = 104 - const val UseOurApp = 105 } object ChannelType { diff --git a/app/src/main/java/com/duckduckgo/app/notification/model/UseOurAppNotification.kt b/app/src/main/java/com/duckduckgo/app/notification/model/UseOurAppNotification.kt deleted file mode 100644 index c74a5cd72c87..000000000000 --- a/app/src/main/java/com/duckduckgo/app/notification/model/UseOurAppNotification.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2020 DuckDuckGo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.duckduckgo.app.notification.model - -import android.content.Context -import android.os.Bundle -import com.duckduckgo.app.browser.R -import com.duckduckgo.app.browser.addtohome.AddToHomeCapabilityDetector -import com.duckduckgo.app.notification.NotificationHandlerService -import com.duckduckgo.app.notification.NotificationHandlerService.NotificationEvent.CANCEL -import com.duckduckgo.app.notification.NotificationRegistrar -import com.duckduckgo.app.notification.db.NotificationDao -import com.duckduckgo.app.pixels.AppPixelName -import com.duckduckgo.app.settings.db.SettingsDataStore -import timber.log.Timber - -class UseOurAppNotification( - private val context: Context, - private val notificationDao: NotificationDao, - private val settingsDataStore: SettingsDataStore, - private val addToHomeCapabilityDetector: AddToHomeCapabilityDetector -) : SchedulableNotification { - - override val id = ID - override val launchIntent = NotificationHandlerService.NotificationEvent.USE_OUR_APP - override val cancelIntent = CANCEL - - override suspend fun canShow(): Boolean { - if (notificationDao.exists(id) || settingsDataStore.hideTips || !addToHomeCapabilityDetector.isAddToHomeSupported()) { - Timber.v("Notification already seen") - return false - } - - return true - } - - override suspend fun buildSpecification(): NotificationSpec { - return UseOurAppSpecification(context) - } - - companion object { - const val ID = "com.duckduckgo.privacytips.useOurApp" - } -} - -class UseOurAppSpecification(context: Context) : NotificationSpec { - override val channel = NotificationRegistrar.ChannelType.TUTORIALS - override val systemId = NotificationRegistrar.NotificationId.UseOurApp - override val name = "Use our app" - override val icon = R.drawable.notification_logo - override val title: String = context.getString(R.string.useOurAppNotificationTitle) - override val description: String = context.getString(R.string.useOurAppNotificationDescription) - override val launchButton: String? = null - override val closeButton: String? = null - override val pixelSuffix = AppPixelName.USE_OUR_APP_NOTIFICATION_SUFFIX.pixelName - override val autoCancel = true - override val bundle: Bundle = Bundle() - override val color: Int = R.color.ic_launcher_red_background -} diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStage.kt b/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStage.kt index 8ebf5c76f568..c46f825cf44d 100644 --- a/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStage.kt +++ b/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStage.kt @@ -31,8 +31,6 @@ data class UserStage( enum class AppStage { NEW, DAX_ONBOARDING, - USE_OUR_APP_NOTIFICATION, - USE_OUR_APP_ONBOARDING, ESTABLISHED; } diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStageStore.kt b/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStageStore.kt index 696ca8142e03..f328a33831c7 100644 --- a/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStageStore.kt +++ b/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStageStore.kt @@ -16,16 +16,9 @@ package com.duckduckgo.app.onboarding.store -import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver -import androidx.lifecycle.OnLifecycleEvent import com.duckduckgo.app.global.DispatcherProvider -import com.duckduckgo.app.global.install.AppInstallStore -import com.duckduckgo.app.statistics.VariantManager -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.concurrent.TimeUnit import javax.inject.Inject interface UserStageStore : LifecycleObserver { @@ -36,17 +29,8 @@ interface UserStageStore : LifecycleObserver { class AppUserStageStore @Inject constructor( private val userStageDao: UserStageDao, - private val dispatcher: DispatcherProvider, - private val variantManager: VariantManager, - private val appInstallStore: AppInstallStore -) : UserStageStore, LifecycleObserver { - - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - fun onAppResumed() { - GlobalScope.launch(dispatcher.io()) { - moveUserToEstablished3DaysAfterInstall() - } - } + private val dispatcher: DispatcherProvider +) : UserStageStore { override suspend fun getUserAppStage(): AppStage { return withContext(dispatcher.io()) { @@ -60,8 +44,6 @@ class AppUserStageStore @Inject constructor( val newAppStage = when (appStage) { AppStage.NEW -> AppStage.DAX_ONBOARDING AppStage.DAX_ONBOARDING -> AppStage.ESTABLISHED - AppStage.USE_OUR_APP_NOTIFICATION -> AppStage.ESTABLISHED - AppStage.USE_OUR_APP_ONBOARDING -> AppStage.ESTABLISHED AppStage.ESTABLISHED -> AppStage.ESTABLISHED } @@ -77,16 +59,6 @@ class AppUserStageStore @Inject constructor( userStageDao.updateUserStage(appStage) } - private suspend fun moveUserToEstablished3DaysAfterInstall() { - if (variantManager.getVariant().hasFeature(VariantManager.VariantFeature.KillOnboarding)) { - if (appInstallStore.hasInstallTimestampRecorded() && daxOnboardingActive()) { - val days = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - appInstallStore.installTimestamp) - if (days >= 3) { - moveToStage(AppStage.ESTABLISHED) - } - } - } - } } suspend fun UserStageStore.isNewUser(): Boolean { @@ -96,7 +68,3 @@ suspend fun UserStageStore.isNewUser(): Boolean { suspend fun UserStageStore.daxOnboardingActive(): Boolean { return this.getUserAppStage() == AppStage.DAX_ONBOARDING } - -suspend fun UserStageStore.useOurAppOnboarding(): Boolean { - return this.getUserAppStage() == AppStage.USE_OUR_APP_ONBOARDING -} diff --git a/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt b/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt index c08bd27a1a7d..0585d4f7f066 100644 --- a/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt +++ b/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt @@ -177,17 +177,6 @@ enum class AppPixelName(override val pixelName: String) : Pixel.PixelName { FIREPROOF_LOGIN_DISABLE_DIALOG_DISABLE("m_fw_dd_d"), FIREPROOF_LOGIN_DISABLE_DIALOG_CANCEL("m_fw_dd_c"), - USE_OUR_APP_NOTIFICATION_SUFFIX("uoa"), - USE_OUR_APP_DIALOG_SHOWN("m_uoa_d"), - USE_OUR_APP_DIALOG_OK("m_uoa_d_ok"), - USE_OUR_APP_SHORTCUT_ADDED("m_uoa_s_a"), - USE_OUR_APP_DIALOG_DELETE_SHOWN("m_uoa_dd"), - UOA_VISITED_AFTER_SHORTCUT("m_uoa_vas"), - UOA_VISITED_AFTER_NOTIFICATION("m_uoa_van"), - UOA_VISITED_AFTER_DELETE_CTA("m_uoa_vad"), - UOA_VISITED("m_uoa_v"), - - USE_OUR_APP_SHORTCUT_OPENED("m_sho_uoa_o"), SHORTCUT_ADDED("m_sho_a"), SHORTCUT_OPENED("m_sho_o"), diff --git a/app/src/main/java/com/duckduckgo/app/tabs/model/TabDataRepository.kt b/app/src/main/java/com/duckduckgo/app/tabs/model/TabDataRepository.kt index 8adbd593da9d..63daef1fc27e 100644 --- a/app/src/main/java/com/duckduckgo/app/tabs/model/TabDataRepository.kt +++ b/app/src/main/java/com/duckduckgo/app/tabs/model/TabDataRepository.kt @@ -24,7 +24,6 @@ import com.duckduckgo.app.browser.tabpreview.WebViewPreviewPersister import com.duckduckgo.app.di.AppCoroutineScope import com.duckduckgo.app.global.model.Site import com.duckduckgo.app.global.model.SiteFactory -import com.duckduckgo.app.global.useourapp.UseOurAppDetector import com.duckduckgo.app.tabs.db.TabsDao import io.reactivex.Scheduler import io.reactivex.schedulers.Schedulers @@ -45,7 +44,6 @@ class TabDataRepository @Inject constructor( private val siteFactory: SiteFactory, private val webViewPreviewPersister: WebViewPreviewPersister, private val faviconManager: FaviconManager, - private val useOurAppDetector: UseOurAppDetector, @AppCoroutineScope private val appCoroutineScope: CoroutineScope ) : TabRepository { @@ -146,13 +144,8 @@ class TabDataRepository @Inject constructor( } override suspend fun selectByUrlOrNewTab(url: String) { - val query = if (useOurAppDetector.isUseOurAppUrl(url)) { - UseOurAppDetector.USE_OUR_APP_DOMAIN_QUERY - } else { - url - } - val tabId = tabsDao.selectTabByUrl(query) + val tabId = tabsDao.selectTabByUrl(url) if (tabId != null) { select(tabId) } else { diff --git a/app/src/main/res/values/string-untranslated.xml b/app/src/main/res/values/string-untranslated.xml index f0155860be8f..fbc6cdbad16b 100644 --- a/app/src/main/res/values/string-untranslated.xml +++ b/app/src/main/res/values/string-untranslated.xml @@ -15,16 +15,6 @@ --> - - - Did you know the Facebook app can make requests for data even when you\'re not using it?<br/><br/>Replace the app with a home screen shortcut that opens Facebook in DuckDuckGo. Then delete the Facebook app. - Add Facebook Shortcut - Not Now - Worried about Facebook tracking you? - Here\'s a simple way to reduce its reach. - Success! %s has been added to your home screen. - Checking your feed in DuckDuckGo is a great alternative to using the Facebook app!<br/><br/>But if the Facebook app is on your phone, it can make requests for data even when you\'re not using it.<br/><br/>Prevent this by deleting it now! - Email Autofill Enabled for %1$s @@ -40,4 +30,8 @@ We only use your anonymous location to deliver better results, closer to you. You can always change your mind later. + + + Success! %s has been added to your home screen. + diff --git a/statistics/src/main/java/com/duckduckgo/app/statistics/VariantManager.kt b/statistics/src/main/java/com/duckduckgo/app/statistics/VariantManager.kt index 29a6d040e41b..091dc27975bf 100644 --- a/statistics/src/main/java/com/duckduckgo/app/statistics/VariantManager.kt +++ b/statistics/src/main/java/com/duckduckgo/app/statistics/VariantManager.kt @@ -28,9 +28,7 @@ interface VariantManager { // variant-dependant features listed here sealed class VariantFeature { - object InAppUsage : VariantFeature() - object KillOnboarding : VariantFeature() - object RemoveDay1AndDay3Notifications : VariantFeature() + } companion object { @@ -45,14 +43,6 @@ interface VariantManager { // the future if we can filter by app version Variant(key = "sc", weight = 0.0, features = emptyList(), filterBy = { isSerpRegionToggleCountry() }), Variant(key = "se", weight = 0.0, features = emptyList(), filterBy = { isSerpRegionToggleCountry() }), - - // InAppUsage Experiments - Variant(key = "ma", weight = 0.0, features = emptyList(), filterBy = { isEnglishLocale() }), - Variant(key = "mb", weight = 0.0, features = listOf(VariantFeature.KillOnboarding, VariantFeature.RemoveDay1AndDay3Notifications), filterBy = { isEnglishLocale() }), - Variant(key = "mc", weight = 0.0, features = listOf(VariantFeature.KillOnboarding, VariantFeature.InAppUsage, VariantFeature.RemoveDay1AndDay3Notifications), filterBy = { isEnglishLocale() }), - - Variant(key = "zx", weight = 1.0, features = emptyList(), filterBy = { isEnglishLocale() }), - Variant(key = "zy", weight = 1.0, features = listOf(VariantFeature.KillOnboarding, VariantFeature.InAppUsage), filterBy = { isEnglishLocale() }) ) val REFERRER_VARIANTS = listOf( From fc600bdb64b89e68f730aa0aac6ceba3cf65b6af Mon Sep 17 00:00:00 2001 From: Marcos Holgado Date: Wed, 19 May 2021 08:53:53 +0100 Subject: [PATCH 2/4] Use correct string --- app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt b/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt index b89bf61e6329..ff864c8203c0 100644 --- a/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt +++ b/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt @@ -318,7 +318,7 @@ class MigrationsProvider(val context: Context) { val MIGRATION_22_TO_23: Migration = object : Migration(22, 23) { override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("UPDATE $USER_STAGE_TABLE_NAME SET appStage = \"${AppStage.ESTABLISHED}\" WHERE appStage = \"AppStage.USE_OUR_APP_NOTIFICATION\"") + database.execSQL("UPDATE $USER_STAGE_TABLE_NAME SET appStage = \"${AppStage.ESTABLISHED}\" WHERE appStage = \"USE_OUR_APP_NOTIFICATION\"") } } From 8c3fb4683c04f3b2f2625c1dcf13dbbbdf886b89 Mon Sep 17 00:00:00 2001 From: Marcos Holgado Date: Wed, 19 May 2021 17:35:09 +0100 Subject: [PATCH 3/4] Add tests for db migration --- .../app/global/db/AppDatabaseTest.kt | 69 ++++++++++++++++++- .../duckduckgo/app/global/db/AppDatabase.kt | 1 + .../app/onboarding/store/UserStage.kt | 6 +- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/com/duckduckgo/app/global/db/AppDatabaseTest.kt b/app/src/androidTest/java/com/duckduckgo/app/global/db/AppDatabaseTest.kt index 4a98dab81cc7..1d130d71021f 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/global/db/AppDatabaseTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/global/db/AppDatabaseTest.kt @@ -260,7 +260,7 @@ class AppDatabaseTest { @Test fun whenMigratingFromVersion22To23IfUserStageIsUseOurAppNotificationThenMigrateToEstablished() { testHelper.createDatabase(TEST_DB_NAME, 22).use { - it.execSQL("INSERT INTO `userStage` values (1, 'USE_OUR_APP_NOTIFICATION') ") + givenUserStageIs(it, "USE_OUR_APP_NOTIFICATION") testHelper.runMigrationsAndValidate(TEST_DB_NAME, 23, true, migrationsProvider.MIGRATION_22_TO_23) val stage = getUserStage(it) @@ -354,10 +354,77 @@ class AppDatabaseTest { createDatabaseAndMigrate(32, 33, migrationsProvider.MIGRATION_32_TO_33) } + @Test + fun whenMigratingFromVersion33o34ThenValidationSucceeds() { + createDatabaseAndMigrate(33, 34, migrationsProvider.MIGRATION_33_TO_34) + } + + @Test + fun whenMigratingFromVersion33To34IfUserStageIsUseOurAppNotificationThenMigrateToEstablished() { + testHelper.createDatabase(TEST_DB_NAME, 33).use { + givenUserStageIs(it, "USE_OUR_APP_NOTIFICATION") + + testHelper.runMigrationsAndValidate(TEST_DB_NAME, 34, true, migrationsProvider.MIGRATION_33_TO_34) + val stage = getUserStage(it) + + assertEquals(AppStage.ESTABLISHED.name, stage) + } + } + + @Test + fun whenMigratingFromVersion33To34IfUserStageIsUseOurAppOnboardingThenMigrateToEstablished() { + testHelper.createDatabase(TEST_DB_NAME, 33).use { + givenUserStageIs(it, "USE_OUR_APP_ONBOARDING") + + testHelper.runMigrationsAndValidate(TEST_DB_NAME, 34, true, migrationsProvider.MIGRATION_33_TO_34) + val stage = getUserStage(it) + + assertEquals(AppStage.ESTABLISHED.name, stage) + } + } + + @Test + fun whenMigratingFromVersion33To34IfUseOurAppEventsExistThenEventsAreDeletedFromDatabase() { + testHelper.createDatabase(TEST_DB_NAME, 33).use { + it.execSQL("INSERT INTO `user_events` values ('USE_OUR_APP_SHORTCUT_ADDED', 1) ") + it.execSQL("INSERT INTO `user_events` values ('USE_OUR_APP_FIREPROOF_DIALOG_SEEN', 1) ") + + testHelper.runMigrationsAndValidate(TEST_DB_NAME, 34, true, migrationsProvider.MIGRATION_33_TO_34) + + val eventsCount = it.query("select count(*) from user_events").run { + moveToFirst() + getInt(0) + } + + assertEquals(0, eventsCount) + } + } + + @Test + fun whenMigratingFromVersion33To34IfUseOurAppCtasExistThenCtasAreDeletedFromDatabase() { + testHelper.createDatabase(TEST_DB_NAME, 33).use { + it.execSQL("INSERT INTO `dismissed_cta` values ('USE_OUR_APP') ") + it.execSQL("INSERT INTO `dismissed_cta` values ('USE_OUR_APP_DELETION') ") + + testHelper.runMigrationsAndValidate(TEST_DB_NAME, 34, true, migrationsProvider.MIGRATION_33_TO_34) + + val ctaCount = it.query("select count(*) from dismissed_cta").run { + moveToFirst() + getInt(0) + } + + assertEquals(0, ctaCount) + } + } + private fun givenUserStageIs(database: SupportSQLiteDatabase, appStage: AppStage) { database.execSQL("INSERT INTO `userStage` values (1, '${appStage.name}') ") } + private fun givenUserStageIs(database: SupportSQLiteDatabase, appStage: String) { + database.execSQL("INSERT INTO `userStage` values (1, '$appStage') ") + } + private fun getUserStage(database: SupportSQLiteDatabase): String { var stage: String diff --git a/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt b/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt index ff864c8203c0..af5351a7ff6b 100644 --- a/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt +++ b/app/src/main/java/com/duckduckgo/app/global/db/AppDatabase.kt @@ -412,6 +412,7 @@ class MigrationsProvider(val context: Context) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("UPDATE $USER_STAGE_TABLE_NAME SET appStage = \"${AppStage.ESTABLISHED}\" WHERE appStage = \"USE_OUR_APP_NOTIFICATION\" OR appStage = \"USE_OUR_APP_ONBOARDING\"") database.execSQL("DELETE FROM user_events WHERE id = \"USE_OUR_APP_SHORTCUT_ADDED\" OR id = \"USE_OUR_APP_FIREPROOF_DIALOG_SEEN\"") + database.execSQL("DELETE FROM dismissed_cta WHERE ctaId = \"USE_OUR_APP\" OR ctaId = \"USE_OUR_APP_DELETION\"") } } diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStage.kt b/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStage.kt index c46f825cf44d..e186366f1ef1 100644 --- a/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStage.kt +++ b/app/src/main/java/com/duckduckgo/app/onboarding/store/UserStage.kt @@ -38,7 +38,11 @@ class StageTypeConverter { @TypeConverter fun toStage(stage: String): AppStage { - return AppStage.valueOf(stage) + return try { + AppStage.valueOf(stage) + } catch (ex: IllegalArgumentException) { + AppStage.ESTABLISHED + } } @TypeConverter From c1602eb1bcfa211234cf5667d8ad52b619bf98b0 Mon Sep 17 00:00:00 2001 From: Marcos Holgado Date: Mon, 24 May 2021 17:26:33 +0100 Subject: [PATCH 4/4] delete unused drawable --- app/src/main/res/drawable/ic_fb_favicon.png | Bin 1779 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_fb_favicon.png diff --git a/app/src/main/res/drawable/ic_fb_favicon.png b/app/src/main/res/drawable/ic_fb_favicon.png deleted file mode 100644 index 32416f0e52327a82548ddace6dd4543842876578..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1779 zcmaJ?dpJ~S9G(;vtCYwkIgKtX=02E{`wV85vD2U^UFLFRFmuMtDT7G0BAcgDBuq+H z>B5q&p$(hXWmCCSMA?mYCAV1F>~7gJLi@+s^E~H#-|u^V@B6#G-*b*|e7$tFO|&r> zj4q4G;G!!{{Vvi%zekM@C8CQl;t`1W!68T#PX=M!gfJfhSYqC8hzs$A(UA?1BL*|y zLgXKa1hRcV0W8Mx)IK5P%Qu7D=42!?&uifJo?s-9lv(*it$a zDq_aSAio%2e?d&RfF{JcI0KFf5G4>p2oF$*BP4QA;e?&y1<}3w7>@;}T##@l?8l@6 z*&Kil%OHS?quL4xL<(R}!x5=OiUZLGAQ6aUJb{cSl5L43kU#^8RABnSqS0i+5Rl98 zoQ{QBPS{Wck%D-9R8$l$ij0G0yYWOCjfN+X@FbEg>R~I7mLNQZtwe4y!@z*#0+~pP zh+qkzX5{hVJ%|$)&GcglV(BccL_Xanv|)G!Pl_kv2BXR0Xn!43w03aKua(IiWBD%R^x)K&JziY@R?QQQKBi z05%(BN#qDmB7j&7CoD>V6N!YN1KEw@LAGa5?1%&+(Szbfu_w?yC=3#V>gmp~cbma7 zV8I?SBtd4d!cSO_Ik9Rjh^1&`1|$>hg@m3mSPV?n42tH?#eGh`X{>PWT6s%`KI-kPZc$@a`H%#ayA0KJ3<)}jUY8IS!_8mOF_3ldOZ`nNYOBuNAjdT= z^sv-jO3l-`yNI{Wvn{LsPP&OZzq?i$+3!-_lGyy}L8gjQn`phVf_OL8$lQn$=FSu? z(}-i6xArL>YA)B`*t*Ng?5Eq1VPb3F-$TmS%HbS0Fm~|#5j%6zl4`x<8eCT}T%}ZO zDiG!I{dq>~F9+z4<*g}MIr$>#?$6e%jZ1Ax9uCZBR!A$FA}SYbx7S(oN&%r_1bNKqZePCRF@v) zb3UKmibM!?n7v=9CJ{sPN}R>2G0?QM9+!hDSno9v89~@e_8n2ZTQ>9t+ok`qD)z%` zoseEbr4H+6XkkS4uW6CJwb8-lim8`?<;n$R^8o&)3M{itKnPzXQ6JY1&%dyiV1Gxpijr zm{xA(H}3}D+rJ!tmGZ9Hc)YVH#2aWh8~rRhPeL6{Bn>&of8915pNBW(pIlz1qa#-eqECezmQE zGWl(pma;G3CTN3Iq4BZBmhbCs9a8LtQ?`sw`rOG_7mn-;W_FJ^dUx49m7ExuIEw@^ zt0n21?^Rt7>}*oSh6QyOX5Ej=C_Ck2p|tH7>Ws_Xe)3Atws6((n!;dj(thu^!cO;= z9X?lu=PZk!xTbx@sZs5454AjWou4(H-(6Uh#%^YHY%r*)zEk`-i*;a(4OtV6jWhB-$C*8a)Jy3^nd9M=OABmWCMROGPuk}K%SX3X zn8o+Pz|oM#1S<|nLBi+aXz ptUrG3&${HsC472n{8RIJS{R+p4mGUkjvMOV5X-}tQRKes;6IqKzaans