diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
commit | d8bbc7858622b6d9c278469aab701ca0b609cddf (patch) | |
tree | eff41dc61d9f714852212739e6b3738b82a2af87 /mobile/android/android-components/components/feature/share | |
parent | Releasing progress-linux version 125.0.3-1~progress7.99u1. (diff) | |
download | firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/android-components/components/feature/share')
14 files changed, 647 insertions, 0 deletions
diff --git a/mobile/android/android-components/components/feature/share/README.md b/mobile/android/android-components/components/feature/share/README.md new file mode 100644 index 0000000000..6f188d49d9 --- /dev/null +++ b/mobile/android/android-components/components/feature/share/README.md @@ -0,0 +1,19 @@ +# [Android Components](../../../README.md) > Feature > Recents + +Feature implementation for saving and sorting recent apps used for sharing. + +## Usage + +### Setting up the dependency + +Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)): + +```Groovy +implementation "org.mozilla.components:feature-share:{latest-version}" +``` + +## License + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/ diff --git a/mobile/android/android-components/components/feature/share/build.gradle b/mobile/android/android-components/components/feature/share/build.gradle new file mode 100644 index 0000000000..a44df774e1 --- /dev/null +++ b/mobile/android/android-components/components/feature/share/build.gradle @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'com.google.devtools.ksp' + +android { + defaultConfig { + minSdkVersion config.minSdkVersion + compileSdk config.compileSdkVersion + targetSdkVersion config.targetSdkVersion + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + ksp { + arg("room.schemaLocation", "$projectDir/schemas".toString()) + arg("room.generateKotlin", "true") + } + + javaCompileOptions { + annotationProcessorOptions { + arguments += ["room.incremental": "true"] + } + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + packagingOptions { + exclude 'META-INF/proguard/androidx-annotations.pro' + } + + sourceSets { + androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) + } + + namespace 'mozilla.components.feature.share' +} + +dependencies { + implementation project(':support-ktx') + implementation project(':support-base') + + implementation ComponentsDependencies.kotlin_coroutines + + implementation ComponentsDependencies.androidx_lifecycle_runtime + implementation ComponentsDependencies.androidx_room_runtime + ksp ComponentsDependencies.androidx_room_compiler + + testImplementation project(':support-test') + + testImplementation ComponentsDependencies.androidx_test_core + testImplementation ComponentsDependencies.testing_junit + testImplementation ComponentsDependencies.testing_robolectric + testImplementation ComponentsDependencies.testing_coroutines + + androidTestImplementation project(':support-android-test') + + androidTestImplementation ComponentsDependencies.androidx_room_testing + androidTestImplementation ComponentsDependencies.androidx_test_core + androidTestImplementation ComponentsDependencies.androidx_test_runner + androidTestImplementation ComponentsDependencies.androidx_test_rules +} + +apply from: '../../../android-lint.gradle' +apply from: '../../../publish.gradle' +ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description) diff --git a/mobile/android/android-components/components/feature/share/proguard-rules.pro b/mobile/android/android-components/components/feature/share/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/mobile/android/android-components/components/feature/share/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/android/android-components/components/feature/share/schemas/mozilla.components.feature.share.db.RecentAppsDatabase/1.json b/mobile/android/android-components/components/feature/share/schemas/mozilla.components.feature.share.db.RecentAppsDatabase/1.json new file mode 100644 index 0000000000..6a26877289 --- /dev/null +++ b/mobile/android/android-components/components/feature/share/schemas/mozilla.components.feature.share.db.RecentAppsDatabase/1.json @@ -0,0 +1,40 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "05028edca931d077160a0d3a3e19b20f", + "entities": [ + { + "tableName": "RECENT_APPS_TABLE", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`packageName` TEXT NOT NULL, `score` REAL NOT NULL, PRIMARY KEY(`packageName`))", + "fields": [ + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "score", + "columnName": "score", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "packageName" + ], + "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, '05028edca931d077160a0d3a3e19b20f')" + ] + } +} diff --git a/mobile/android/android-components/components/feature/share/schemas/mozilla.components.feature.share.db.RecentAppsDatabase/2.json b/mobile/android/android-components/components/feature/share/schemas/mozilla.components.feature.share.db.RecentAppsDatabase/2.json new file mode 100644 index 0000000000..241b612fa9 --- /dev/null +++ b/mobile/android/android-components/components/feature/share/schemas/mozilla.components.feature.share.db.RecentAppsDatabase/2.json @@ -0,0 +1,40 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "8ab7d915ce81c5d52503917cbafbe4df", + "entities": [ + { + "tableName": "RECENT_APPS_TABLE", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`activityName` TEXT NOT NULL, `score` REAL NOT NULL, PRIMARY KEY(`activityName`))", + "fields": [ + { + "fieldPath": "activityName", + "columnName": "activityName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "score", + "columnName": "score", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "activityName" + ], + "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, '8ab7d915ce81c5d52503917cbafbe4df')" + ] + } +} diff --git a/mobile/android/android-components/components/feature/share/src/androidTest/java/mozilla/components/feature/share/RecentAppsDaoTest.kt b/mobile/android/android-components/components/feature/share/src/androidTest/java/mozilla/components/feature/share/RecentAppsDaoTest.kt new file mode 100644 index 0000000000..b0986964f1 --- /dev/null +++ b/mobile/android/android-components/components/feature/share/src/androidTest/java/mozilla/components/feature/share/RecentAppsDaoTest.kt @@ -0,0 +1,105 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.share + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import mozilla.components.feature.share.db.RecentAppEntity +import mozilla.components.feature.share.db.RecentAppsDao +import mozilla.components.feature.share.db.RecentAppsDatabase +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test + +class RecentAppsDaoTest { + + private lateinit var database: RecentAppsDatabase + private lateinit var dao: RecentAppsDao + + @Before + fun setup() { + val context = ApplicationProvider.getApplicationContext<Context>() + database = Room.inMemoryDatabaseBuilder(context, RecentAppsDatabase::class.java).build() + dao = database.recentAppsDao() + } + + @After + fun tearDown() { + database.close() + } + + @Test + fun testGetTwoMostRecentApps() { + assertEquals(emptyList<RecentApp>(), dao.getRecentAppsUpTo(2)) + + dao.insertRecentApps( + listOf( + RecentAppEntity("first"), + RecentAppEntity("second"), + RecentAppEntity("third"), + ), + ) + + assertEquals( + listOf( + RecentAppEntity("first"), + RecentAppEntity("second"), + ), + dao.getRecentAppsUpTo(2), + ) + } + + @Test + fun testIncrementSelectedAppCountAndDecayAllOthers() { + val activityName = "activityName" + val selectedAppEntity = RecentAppEntity(score = 1.0, activityName = activityName) + val otherAppEntity = RecentAppEntity(score = 100.0, activityName = "other") + val allRecentApps = listOf(selectedAppEntity, otherAppEntity) + + dao.insertRecentApps(allRecentApps) + + dao.updateRecentAppAndDecayRest(activityName) + + val allAppsResult = dao.getRecentAppsUpTo(2) + val selectedResult = allAppsResult.first { it.activityName == activityName } + val otherResult = allAppsResult.first { it.activityName == "other" } + + assertEquals(95.0, otherResult.score, 0.0001) + assertEquals(2.0, selectedResult.score, 0.0001) + } + + @Test + fun testAddNewlyInstalledAppsToOurDatabase() { + val firstActivityName = "first" + val secondActivityName = "second" + val thirdActivityName = "third" + val fourthActivityName = "fourth" + val appsInDatabase = listOf( + RecentAppEntity(firstActivityName), + RecentAppEntity(secondActivityName), + RecentAppEntity(thirdActivityName), + ) + val additionalApp = RecentAppEntity(fourthActivityName) + + dao.insertRecentApps(appsInDatabase) + dao.insertRecentApps(appsInDatabase + additionalApp) + + assertEquals(appsInDatabase + additionalApp, dao.getRecentAppsUpTo(7)) + } + + @Test + fun testDeleteAnAppFromOurDatabase() { + val deleteAppName = "delete" + val recentApp = RecentAppEntity(score = 1.0, activityName = deleteAppName) + + dao.insertRecentApps(listOf(recentApp)) + + dao.deleteRecentApp(recentApp.activityName) + + assertEquals(emptyList<RecentApp>(), dao.getRecentAppsUpTo(1)) + } +} diff --git a/mobile/android/android-components/components/feature/share/src/main/AndroidManifest.xml b/mobile/android/android-components/components/feature/share/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..e16cda1d34 --- /dev/null +++ b/mobile/android/android-components/components/feature/share/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<manifest /> diff --git a/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/RecentApp.kt b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/RecentApp.kt new file mode 100644 index 0000000000..cacfdc8192 --- /dev/null +++ b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/RecentApp.kt @@ -0,0 +1,24 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.share + +/** + * Interface used for adapting recent apps database entities + * + * @property activityName - unique identifier of the app + * @property score - value used for sorting in descending order the recent apps (most recent first) + */ +interface RecentApp { + + /** + * The activityName of the recent app. + */ + val activityName: String + + /** + * The score of the recent app (calculated based on number of selections - decay) + */ + val score: Double +} diff --git a/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/RecentAppsStorage.kt b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/RecentAppsStorage.kt new file mode 100644 index 0000000000..563eff85d9 --- /dev/null +++ b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/RecentAppsStorage.kt @@ -0,0 +1,60 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.share + +import android.content.Context +import androidx.annotation.VisibleForTesting +import mozilla.components.feature.share.db.RecentAppEntity +import mozilla.components.feature.share.db.RecentAppsDao +import mozilla.components.feature.share.db.RecentAppsDatabase + +/** + * Class used for storing and retrieving the most recent apps + */ +class RecentAppsStorage(context: Context) { + + @VisibleForTesting + internal var recentAppsDao: Lazy<RecentAppsDao> = lazy { + RecentAppsDatabase.get(context).recentAppsDao() + } + + /** + * Increment the value stored in the database for the selected app. Then, apply a decay to all + * other apps in the database. This allows newly installed apps to catch up and appear in the + * most recent section faster. We do not need to handle overflow as it's not reasonably expected + * to reach Double.MAX_VALUE for users + */ + fun updateRecentApp(selectedActivityName: String) { + recentAppsDao.value.updateRecentAppAndDecayRest(selectedActivityName) + } + + /** + * Deletes an app form the recent apps list + * @param activityName - name of the activity of the app + */ + fun deleteRecentApp(activityName: String) { + recentAppsDao.value.deleteRecentApp(activityName) + } + + /** + * Get a descending ordered list of the most recent apps + * @param limit - size of list + */ + fun getRecentAppsUpTo(limit: Int): List<RecentApp> { + return recentAppsDao.value.getRecentAppsUpTo(limit) + } + + /** + * If there are apps that could resolve our share and are not added in our database, we add them + * with a 0 count, so they can be updated later when a user uses that app + */ + fun updateDatabaseWithNewApps(activityNames: List<String>) { + recentAppsDao.value.insertRecentApps( + activityNames.map { activityName -> + RecentAppEntity(activityName) + }, + ) + } +} diff --git a/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/adapter/RecentAppAdapter.kt b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/adapter/RecentAppAdapter.kt new file mode 100644 index 0000000000..ff0a582b8f --- /dev/null +++ b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/adapter/RecentAppAdapter.kt @@ -0,0 +1,30 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.share.adapter + +import mozilla.components.feature.share.RecentApp +import mozilla.components.feature.share.db.RecentAppEntity + +internal class RecentAppAdapter( + internal val entity: RecentAppEntity, +) : RecentApp { + + override val activityName: String + get() = entity.activityName + + override val score: Double + get() = entity.score + + override fun equals(other: Any?): Boolean { + if (other !is RecentAppAdapter) { + return false + } + return entity == other.entity + } + + override fun hashCode(): Int { + return entity.hashCode() + } +} diff --git a/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/db/RecentAppEntity.kt b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/db/RecentAppEntity.kt new file mode 100644 index 0000000000..c519c91b4b --- /dev/null +++ b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/db/RecentAppEntity.kt @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.share.db + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import mozilla.components.feature.share.RecentApp +import mozilla.components.feature.share.db.RecentAppsDatabase.Companion.RECENT_APPS_TABLE + +@Entity(tableName = RECENT_APPS_TABLE) +internal data class RecentAppEntity( + + @PrimaryKey + @ColumnInfo(name = "activityName") + override var activityName: String, + + @ColumnInfo(name = "score") + override var score: Double = 0.0, +) : RecentApp diff --git a/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/db/RecentAppsDao.kt b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/db/RecentAppsDao.kt new file mode 100644 index 0000000000..9493ca20c0 --- /dev/null +++ b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/db/RecentAppsDao.kt @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.share.db + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Transaction + +private const val DECAY_MULTIPLIER = 0.95 + +@Dao +internal abstract class RecentAppsDao { + + @Insert(onConflict = OnConflictStrategy.IGNORE) + abstract fun insertRecentApps(recentApps: List<RecentAppEntity>) + + @Query( + """ + DELETE FROM recent_apps_table + WHERE activityName = :activityName + """, + ) + abstract fun deleteRecentApp(activityName: String) + + @Query( + """ + SELECT * FROM recent_apps_table + ORDER BY score DESC + LIMIT :limit + """, + ) + abstract fun getRecentAppsUpTo(limit: Int): List<RecentAppEntity> + + /** + * Increments the score of a recent app. + * @param activityName - Name of the recent app to update. + */ + @Query( + """ + UPDATE recent_apps_table + SET score = score + 1 + WHERE activityName = :activityName + """, + ) + abstract fun updateRecentAppScore(activityName: String) + + /** + * Decreases the score of all but one app (exponential decay). + * @param exceptActivity - ID of recent app to leave alone + * @param decay - Amount to decay by. Should be between 0 and 1. + */ + @Query( + """ + UPDATE recent_apps_table + SET score = score * :decay + WHERE activityName != :exceptActivity + """, + ) + abstract fun decayAllRecentApps( + exceptActivity: String, + decay: Double = DECAY_MULTIPLIER, + ) + + @Transaction + open fun updateRecentAppAndDecayRest(activityName: String) { + updateRecentAppScore(activityName) + decayAllRecentApps(exceptActivity = activityName) + } +} diff --git a/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/db/RecentAppsDatabase.kt b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/db/RecentAppsDatabase.kt new file mode 100644 index 0000000000..d554bd3209 --- /dev/null +++ b/mobile/android/android-components/components/feature/share/src/main/java/mozilla/components/feature/share/db/RecentAppsDatabase.kt @@ -0,0 +1,60 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.share.db + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import mozilla.components.feature.share.db.RecentAppsDatabase.Companion.RECENT_APPS_TABLE + +/** + * Internal database for storing apps and their scores that determine if they are most recently used. + */ +@Database(entities = [RecentAppEntity::class], version = 2) +internal abstract class RecentAppsDatabase : RoomDatabase() { + abstract fun recentAppsDao(): RecentAppsDao + + companion object { + + const val RECENT_APPS_TABLE = "RECENT_APPS_TABLE" + + @Volatile + private var instance: RecentAppsDatabase? = null + + @Synchronized + fun get(context: Context): RecentAppsDatabase { + instance?.let { return it } + + return Room.databaseBuilder( + context, + RecentAppsDatabase::class.java, + RECENT_APPS_TABLE, + ).addMigrations( + Migrations.migration_1_2, + ).build().also { + instance = it + } + } + } +} + +internal object Migrations { + val migration_1_2 = object : Migration(1, 2) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("DROP TABLE RECENT_APPS_TABLE") + db.execSQL( + "CREATE TABLE IF NOT EXISTS " + RECENT_APPS_TABLE + + "(" + + "`activityName` TEXT NOT NULL, " + + "`score` DOUBLE NOT NULL, " + + " PRIMARY KEY(`activityName`)" + + ")", + ) + } + } +} diff --git a/mobile/android/android-components/components/feature/share/src/test/java/mozilla/components/feature/share/RecentAppStorageTest.kt b/mobile/android/android-components/components/feature/share/src/test/java/mozilla/components/feature/share/RecentAppStorageTest.kt new file mode 100644 index 0000000000..6a8dee764b --- /dev/null +++ b/mobile/android/android-components/components/feature/share/src/test/java/mozilla/components/feature/share/RecentAppStorageTest.kt @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.feature.share + +import android.content.Context +import mozilla.components.feature.share.db.RecentAppEntity +import mozilla.components.feature.share.db.RecentAppsDao +import mozilla.components.support.test.mock +import mozilla.components.support.test.whenever +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito.verify + +class RecentAppStorageTest { + + private lateinit var context: Context + private lateinit var recentAppsDao: RecentAppsDao + private lateinit var recentAppsStorage: RecentAppsStorage + + @Before + fun setup() { + context = mock() + recentAppsDao = mock() + + recentAppsStorage = RecentAppsStorage(context) + recentAppsStorage.recentAppsDao = lazyOf(recentAppsDao) + } + + @Test + fun `get the two most recent apps`() { + whenever(recentAppsDao.getRecentAppsUpTo(2)).thenReturn(emptyList()) + + assertEquals(emptyList<RecentApp>(), recentAppsStorage.getRecentAppsUpTo(2)) + } + + @Test + fun `increment selected app count and decay all others`() { + val activityName = "activityName" + + recentAppsStorage.updateRecentApp(activityName) + + verify(recentAppsDao).updateRecentAppAndDecayRest(activityName) + } + + @Test + fun `add newly installed apps to our database`() { + val firstActivityName = "first" + val secondActivityName = "second" + val thirdActivityName = "third" + val fourthActivityName = "fourth" + val currentApps = listOf(firstActivityName, secondActivityName, thirdActivityName, fourthActivityName) + val appsInDatabase = listOf( + RecentAppEntity(firstActivityName), + RecentAppEntity(secondActivityName), + RecentAppEntity(thirdActivityName), + RecentAppEntity(fourthActivityName), + ) + + recentAppsStorage.updateDatabaseWithNewApps(currentApps) + + verify(recentAppsDao).insertRecentApps(appsInDatabase) + } + + @Test + fun `delete an app from our database`() { + val deleteAppName = "delete" + val recentApp = RecentAppEntity(score = 1.0, activityName = deleteAppName) + + recentAppsStorage.deleteRecentApp(recentApp.activityName) + + verify(recentAppsDao).deleteRecentApp(deleteAppName) + } +} |