diff --git a/app/src/main/java/com/anod/appwatcher/database/AppListTable.kt b/app/src/main/java/com/anod/appwatcher/database/AppListTable.kt index 301c7026..db7db131 100644 --- a/app/src/main/java/com/anod/appwatcher/database/AppListTable.kt +++ b/app/src/main/java/com/anod/appwatcher/database/AppListTable.kt @@ -90,12 +90,6 @@ interface AppListTable { ) fun observePackagesList(): Flow> - @Query( - "SELECT ${BaseColumns._ID}, ${Columns.PACKAGE_NAME} FROM $TABLE WHERE " + - "${Columns.STATUS} = ${App.STATUS_UPDATED}" - ) - suspend fun loadUpdatedPackages(): List - @Query( "SELECT ${BaseColumns._ID}, ${Columns.PACKAGE_NAME} FROM $TABLE WHERE " + "CASE :includeDeleted WHEN 0 THEN ${Columns.STATUS} != ${App.STATUS_DELETED} ELSE ${Columns.STATUS} >= ${App.STATUS_NORMAL} END" @@ -152,22 +146,6 @@ interface AppListTable { ) suspend fun clearUpdateStatus(rowId: Int): Int - @Query( - "UPDATE $TABLE SET " + - "${Columns.STATUS} = ${App.STATUS_NORMAL}, " + - "${Columns.SYNC_TIMESTAMP} = 0 " + - "WHERE ${Columns.PACKAGE_NAME} = :packageName AND ${Columns.STATUS} = ${App.STATUS_UPDATED}" - ) - suspend fun clearUpdateStatusByPackageName(packageName: String): Int - - @Query( - "UPDATE $TABLE SET " + - "${Columns.STATUS} = ${App.STATUS_NORMAL}, " + - "${Columns.SYNC_TIMESTAMP} = 0 " + - "WHERE ${BaseColumns._ID} IN (:rowIds) AND ${Columns.STATUS} = ${App.STATUS_UPDATED}" - ) - suspend fun clearUpdateStatuses(rowIds: List): Int - @Query( "UPDATE $TABLE SET " + "${Columns.TITLE} = :title, " + diff --git a/app/src/main/java/com/anod/appwatcher/installed/PackageRemovedReceiver.kt b/app/src/main/java/com/anod/appwatcher/installed/PackageRemovedReceiver.kt index 9c824fa4..489e3f56 100644 --- a/app/src/main/java/com/anod/appwatcher/installed/PackageRemovedReceiver.kt +++ b/app/src/main/java/com/anod/appwatcher/installed/PackageRemovedReceiver.kt @@ -4,11 +4,9 @@ package com.anod.appwatcher.installed import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import com.anod.appwatcher.database.AppsDatabase import com.anod.appwatcher.utils.PackageChangedReceiver import com.anod.appwatcher.utils.PackageStateCache import com.anod.appwatcher.utils.appScope -import com.anod.appwatcher.utils.clearDisabledUpdateStatus import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.get @@ -19,38 +17,27 @@ class PackageRemovedReceiver : BroadcastReceiver(), KoinComponent { val action = intent?.action ?: return val packageName = intent.data?.schemeSpecificPart ?: return when (action) { - Intent.ACTION_PACKAGE_FULLY_REMOVED -> { - handlePackageChange(packageName, clearDisabledUpdates = false, requireStateChange = false) - } - Intent.ACTION_PACKAGE_ADDED -> { - handlePackageChange(packageName, clearDisabledUpdates = true, requireStateChange = false) + Intent.ACTION_PACKAGE_FULLY_REMOVED, + Intent.ACTION_PACKAGE_ADDED, + Intent.ACTION_PACKAGE_REPLACED -> { + handlePackageChange(packageName, requireStateChange = false) } Intent.ACTION_PACKAGE_CHANGED -> { if (isApplicationPackageChange(packageName, intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST))) { - handlePackageChange(packageName, clearDisabledUpdates = true, requireStateChange = true) + handlePackageChange(packageName, requireStateChange = true) } } - Intent.ACTION_PACKAGE_REPLACED -> { - handlePackageChange(packageName, clearDisabledUpdates = true, requireStateChange = false) - } } } private fun handlePackageChange( packageName: String, - clearDisabledUpdates: Boolean, requireStateChange: Boolean ) { val pendingResult = goAsync() appScope.launch { try { val packageStateRefresh = get().refresh(packageName) - if (clearDisabledUpdates) { - get().apps().clearDisabledUpdateStatus( - packageName = packageName, - packageState = packageStateRefresh.current - ) - } if (!requireStateChange || packageStateRefresh.changed) { get().emit(packageName + ":" + System.currentTimeMillis()) } diff --git a/app/src/main/java/com/anod/appwatcher/preferences/SettingsViewModel.kt b/app/src/main/java/com/anod/appwatcher/preferences/SettingsViewModel.kt index 624e1f42..d5e6d89b 100644 --- a/app/src/main/java/com/anod/appwatcher/preferences/SettingsViewModel.kt +++ b/app/src/main/java/com/anod/appwatcher/preferences/SettingsViewModel.kt @@ -367,6 +367,7 @@ class SettingsViewModel : BaseFlowViewModel, builder: NotificationCompat.Builder) { - updatedApps.firstOrNull { it.installedVersionCode > 0 && it.versionNumber > it.installedVersionCode } - ?: return - val bigText = updatedApps.joinToString(",\n") { it.title } builder.setStyle(NotificationCompat.BigTextStyle().bigText(bigText)) - val updateIntent = NotificationActivity.intent( - Uri.parse("com.anod.appwatcher://play/myapps/1"), - NotificationActivity.ACTION_MY_APPS, - context.actual) - builder.addAction(R.drawable.ic_system_update_alt_white_24dp, context.getString(R.string.noti_action_update), - PendingIntent.getActivity(context.actual, 0, updateIntent, PendingIntent.FLAG_IMMUTABLE) - ) + if (updatedApps.any { it.canUpdateOnDevice && it.versionNumber > it.installedVersionCode }) { + val updateIntent = NotificationActivity.intent( + Uri.parse("com.anod.appwatcher://play/myapps/1"), + NotificationActivity.ACTION_MY_APPS, + context.actual) + builder.addAction(R.drawable.ic_system_update_alt_white_24dp, context.getString(R.string.noti_action_update), + PendingIntent.getActivity(context.actual, 0, updateIntent, PendingIntent.FLAG_IMMUTABLE) + ) + } val readIntent = NotificationActivity.intent( Uri.parse("com.anod.appwatcher://dismiss/"), @@ -165,7 +164,7 @@ class SyncNotification(private val context: ApplicationContext, private val noti PendingIntent.getActivity(context.actual, 0, playIntent, PendingIntent.FLAG_IMMUTABLE) ) - if (update.installedVersionCode > 0) { + if (update.canUpdateOnDevice) { val updateIntent = NotificationActivity.intent( Uri.parse("com.anod.appwatcher://play/myapps/1"), NotificationActivity.ACTION_MY_APPS, diff --git a/app/src/main/java/com/anod/appwatcher/sync/UpdateCheck.kt b/app/src/main/java/com/anod/appwatcher/sync/UpdateCheck.kt index 7ca8ee12..66234114 100644 --- a/app/src/main/java/com/anod/appwatcher/sync/UpdateCheck.kt +++ b/app/src/main/java/com/anod/appwatcher/sync/UpdateCheck.kt @@ -29,7 +29,6 @@ import com.anod.appwatcher.database.entities.toApp import com.anod.appwatcher.preferences.Preferences import com.anod.appwatcher.utils.PackageState import com.anod.appwatcher.utils.PackageStateCache -import com.anod.appwatcher.utils.clearDisabledUpdateStatuses import com.anod.appwatcher.utils.compareLettersAndDigits import com.anod.appwatcher.utils.date.UploadDateParserCache import com.anod.appwatcher.utils.extractUploadDate @@ -250,15 +249,6 @@ class UpdateCheck( syncId: Long, verboseDiagnostics: Boolean ): SyncResult { - val clearedDisabledUpdates = database.apps().clearDisabledUpdateStatuses( - packageStates = packageStates - ) - if (clearedDisabledUpdates > 0) { - AppLog.i( - "Cleared $clearedDisabledUpdates disabled app update statuses", - "UpdateCheck" - ) - } val sortId = preferences.sortIndex val apps = AppListTable.Queries.loadAppList(false, sortId, database.apps()) if (apps.isEmpty) { @@ -658,8 +648,7 @@ class UpdateCheck( cachedVersion = localApp.versionNumber, installedVersion = installedInfo.versionCode, status = localApp.status, - lastUpdatesViewed = lastUpdatesViewed, - installedEnabled = installedEnabled + lastUpdatesViewed = lastUpdatesViewed ) if (decision == AppUpdateDecision.MARK_UPDATED) { AppLog.i( @@ -678,7 +667,7 @@ class UpdateCheck( val recentChanges = releaseDoc.appDetails.recentChangesHtml ?: "" return AppUpdateResult( values = newApp.contentValues, - updatedApp = UpdatedApp(newApp, recentChanges, installedInfo.versionCode, true), + updatedApp = UpdatedApp(newApp, recentChanges, installedInfo.versionCode, installedEnabled, true), persistChangelog = true, installedVersion = installedInfo.versionCode, installedEnabled = installedEnabled, @@ -691,14 +680,13 @@ class UpdateCheck( AppUpdateDecision.RESTORE_DEVICE_UPDATE -> values.put(AppListTable.Columns.STATUS, App.STATUS_UPDATED) AppUpdateDecision.CLEAR_INSTALLED_UPDATE, - AppUpdateDecision.CLEAR_DISABLED_UPDATE, AppUpdateDecision.CLEAR_VIEWED_UPDATE -> { values.put(AppListTable.Columns.STATUS, App.STATUS_NORMAL) values.put(AppListTable.Columns.SYNC_TIMESTAMP, 0L) } AppUpdateDecision.KEEP_UPDATED -> { val recentChanges = releaseDoc.appDetails.recentChangesHtml ?: "" - updatedApp = UpdatedApp(localApp, recentChanges, installedInfo.versionCode, false) + updatedApp = UpdatedApp(localApp, recentChanges, installedInfo.versionCode, installedEnabled, false) } AppUpdateDecision.MISSING_RESPONSE_KEEP, AppUpdateDecision.RESPONSE_WITHOUT_DETAILS_KEEP, @@ -708,7 +696,6 @@ class UpdateCheck( AppUpdateDecision.MARK_UPDATED, AppUpdateDecision.REFRESH_INSTALLED_CURRENT, AppUpdateDecision.KEEP_DEVICE_UPDATE, - AppUpdateDecision.DISABLED_INSTALLED, AppUpdateDecision.CURRENT -> { } } @@ -920,10 +907,8 @@ internal enum class AppUpdateDecision(val value: String) { RESTORE_DEVICE_UPDATE("restore-device-update"), KEEP_DEVICE_UPDATE("keep-device-update"), CLEAR_INSTALLED_UPDATE("clear-installed-update"), - CLEAR_DISABLED_UPDATE("clear-disabled-update"), CLEAR_VIEWED_UPDATE("clear-viewed-update"), KEEP_UPDATED("keep-updated"), - DISABLED_INSTALLED("disabled-installed"), CURRENT("current"); val isSteadyState: Boolean @@ -932,16 +917,14 @@ internal enum class AppUpdateDecision(val value: String) { this == UNAVAILABLE_SUPPRESSED || this == UNAVAILABLE_ROLLBACK || this == KEEP_DEVICE_UPDATE || - this == KEEP_UPDATED || - this == DISABLED_INSTALLED + this == KEEP_UPDATED } internal enum class SyncDecisionSignal(val value: String) { NONE("none"), INVALID_MARKED_NOT_NEWER_THAN_INSTALLED("invalid-marked-not-newer-than-installed"), DEVICE_UPDATE_RESTORED("device-update-restored"), - INSTALLED_UPDATE_CLEARED("installed-update-cleared"), - DISABLED_UPDATE_CLEARED("disabled-update-cleared") + INSTALLED_UPDATE_CLEARED("installed-update-cleared") } internal fun detectSyncDecisionSignal( @@ -955,8 +938,6 @@ internal fun detectSyncDecisionSignal( SyncDecisionSignal.DEVICE_UPDATE_RESTORED decision == AppUpdateDecision.CLEAR_INSTALLED_UPDATE -> SyncDecisionSignal.INSTALLED_UPDATE_CLEARED - decision == AppUpdateDecision.CLEAR_DISABLED_UPDATE -> - SyncDecisionSignal.DISABLED_UPDATE_CLEARED else -> SyncDecisionSignal.NONE } @@ -965,16 +946,11 @@ internal fun selectAppUpdateDecision( cachedVersion: Int, installedVersion: Int, status: Int, - lastUpdatesViewed: Boolean, - installedEnabled: Boolean + lastUpdatesViewed: Boolean ): AppUpdateDecision { require(remoteVersion >= cachedVersion) val isInstalled = installedVersion > 0 return when { - isInstalled && !installedEnabled && status == App.STATUS_UPDATED -> - AppUpdateDecision.CLEAR_DISABLED_UPDATE - isInstalled && !installedEnabled -> - AppUpdateDecision.DISABLED_INSTALLED isInstalled && remoteVersion <= installedVersion && status == App.STATUS_UPDATED -> AppUpdateDecision.CLEAR_INSTALLED_UPDATE isInstalled && remoteVersion <= installedVersion && remoteVersion > cachedVersion -> diff --git a/app/src/main/java/com/anod/appwatcher/sync/UpdatedApp.kt b/app/src/main/java/com/anod/appwatcher/sync/UpdatedApp.kt index bd032b3e..281b77aa 100644 --- a/app/src/main/java/com/anod/appwatcher/sync/UpdatedApp.kt +++ b/app/src/main/java/com/anod/appwatcher/sync/UpdatedApp.kt @@ -11,10 +11,17 @@ data class UpdatedApp( val uploadDate: String, val recentChanges: String, val installedVersionCode: Int, + val installedEnabled: Boolean, val isNewUpdate: Boolean, val noNewDetails: Boolean = false ) { - constructor(appInfo: App, recentChanges: String, installedVersionCode: Int, isNewUpdate: Boolean) : this( + constructor( + appInfo: App, + recentChanges: String, + installedVersionCode: Int, + installedEnabled: Boolean, + isNewUpdate: Boolean + ) : this( appInfo.packageName, appInfo.versionNumber, appInfo.title, @@ -22,6 +29,10 @@ data class UpdatedApp( appInfo.uploadDate, recentChanges, installedVersionCode, + installedEnabled, isNewUpdate ) + + val canUpdateOnDevice: Boolean + get() = installedVersionCode > 0 && installedEnabled } \ No newline at end of file diff --git a/app/src/main/java/com/anod/appwatcher/utils/PackageEnabledState.kt b/app/src/main/java/com/anod/appwatcher/utils/PackageEnabledState.kt index 684ff682..6438ee98 100644 --- a/app/src/main/java/com/anod/appwatcher/utils/PackageEnabledState.kt +++ b/app/src/main/java/com/anod/appwatcher/utils/PackageEnabledState.kt @@ -3,7 +3,6 @@ package com.anod.appwatcher.utils import android.content.pm.PackageInfo import android.content.pm.PackageManager import androidx.core.content.pm.PackageInfoCompat -import com.anod.appwatcher.database.AppListTable import info.anodsplace.framework.content.InstalledApps import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.sync.Mutex @@ -183,36 +182,4 @@ class PackageStateCache(private val packageManager: PackageManager) : PackageSta } internal fun Map.stateFor(packageName: String): PackageState = - get(packageName) ?: PackageState.NotInstalled - -internal suspend fun AppListTable.clearDisabledUpdateStatus( - packageName: String, - packageState: PackageState -): Int = withContext(Dispatchers.IO) { - if (packageState.isInstalled && !packageState.isEnabled) { - clearUpdateStatusByPackageName(packageName) - } else { - 0 - } -} - -internal suspend fun AppListTable.clearDisabledUpdateStatuses( - packageStates: PackageStateProvider -): Int = withContext(Dispatchers.IO) { - val updatedPackages = loadUpdatedPackages() - if (updatedPackages.isEmpty()) { - return@withContext 0 - } - val states = packageStates.load(updatedPackages.map { it.packageName }) - val disabledRowIds = updatedPackages.mapNotNull { row -> - val packageState = states.stateFor(row.packageName) - row.rowId.takeIf { - packageState.isInstalled && !packageState.isEnabled - } - } - var cleared = 0 - for (rowIds in disabledRowIds.chunked(998)) { - cleared += clearUpdateStatuses(rowIds) - } - cleared -} \ No newline at end of file + get(packageName) ?: PackageState.NotInstalled \ No newline at end of file diff --git a/app/src/main/java/com/anod/appwatcher/watchlist/WatchListStateViewModel.kt b/app/src/main/java/com/anod/appwatcher/watchlist/WatchListStateViewModel.kt index 4b142b89..61dbebe8 100644 --- a/app/src/main/java/com/anod/appwatcher/watchlist/WatchListStateViewModel.kt +++ b/app/src/main/java/com/anod/appwatcher/watchlist/WatchListStateViewModel.kt @@ -32,7 +32,6 @@ import com.anod.appwatcher.utils.PackageChangedReceiver import com.anod.appwatcher.utils.PackageStateCache import com.anod.appwatcher.utils.SyncProgress import com.anod.appwatcher.utils.appScope -import com.anod.appwatcher.utils.clearDisabledUpdateStatuses import com.anod.appwatcher.utils.color.MaterialColors import com.anod.appwatcher.utils.forMyApps import com.anod.appwatcher.utils.getInt @@ -210,16 +209,6 @@ class WatchListStateViewModel( AppLog.d("Initial state: viewState") - viewModelScope.launch { - val clearedDisabledUpdates = db.apps().clearDisabledUpdateStatuses( - packageStates = packageStates - ) - if (clearedDisabledUpdates > 0) { - invalidatePagingSources() - viewState = viewState.copy(dbAppsChange = viewState.dbAppsChange + 1) - } - } - viewModelScope.launch { syncProgressFlow(application).collect { handleEvent(WatchListEvent.UpdateSyncProgress(syncProgress = it)) diff --git a/app/src/test/java/com/anod/appwatcher/details/AppItemStateTest.kt b/app/src/test/java/com/anod/appwatcher/details/AppItemStateTest.kt index 8304cd60..ea5563b2 100644 --- a/app/src/test/java/com/anod/appwatcher/details/AppItemStateTest.kt +++ b/app/src/test/java/com/anod/appwatcher/details/AppItemStateTest.kt @@ -33,6 +33,7 @@ class AppItemStateTest { assertTrue(state.installed) assertFalse(state.showRecent) + assertEquals(Color.Black, state.color) assertEquals( context.getString(R.string.installed_disabled_version, "1.0", 100), state.text diff --git a/app/src/test/java/com/anod/appwatcher/sync/UpdateCheckVersionRollbackTest.kt b/app/src/test/java/com/anod/appwatcher/sync/UpdateCheckVersionRollbackTest.kt index 1b24a5ce..ec05912c 100644 --- a/app/src/test/java/com/anod/appwatcher/sync/UpdateCheckVersionRollbackTest.kt +++ b/app/src/test/java/com/anod/appwatcher/sync/UpdateCheckVersionRollbackTest.kt @@ -59,8 +59,7 @@ class UpdateCheckVersionRollbackTest { cachedVersion = 100, installedVersion = 101, status = App.STATUS_NORMAL, - lastUpdatesViewed = false, - installedEnabled = true + lastUpdatesViewed = false ) ) } @@ -74,8 +73,7 @@ class UpdateCheckVersionRollbackTest { cachedVersion = 101, installedVersion = 100, status = App.STATUS_NORMAL, - lastUpdatesViewed = true, - installedEnabled = true + lastUpdatesViewed = true ) ) } @@ -89,8 +87,7 @@ class UpdateCheckVersionRollbackTest { cachedVersion = 101, installedVersion = 100, status = App.STATUS_UPDATED, - lastUpdatesViewed = true, - installedEnabled = true + lastUpdatesViewed = true ) ) } @@ -104,8 +101,7 @@ class UpdateCheckVersionRollbackTest { cachedVersion = 101, installedVersion = 101, status = App.STATUS_UPDATED, - lastUpdatesViewed = false, - installedEnabled = true + lastUpdatesViewed = false ) ) } @@ -119,8 +115,7 @@ class UpdateCheckVersionRollbackTest { cachedVersion = 101, installedVersion = 0, status = App.STATUS_UPDATED, - lastUpdatesViewed = true, - installedEnabled = false + lastUpdatesViewed = true ) ) } @@ -134,8 +129,7 @@ class UpdateCheckVersionRollbackTest { cachedVersion = 101, installedVersion = 0, status = App.STATUS_UPDATED, - lastUpdatesViewed = false, - installedEnabled = false + lastUpdatesViewed = false ) ) } @@ -149,38 +143,50 @@ class UpdateCheckVersionRollbackTest { cachedVersion = 101, installedVersion = 101, status = App.STATUS_NORMAL, - lastUpdatesViewed = true, - installedEnabled = true + lastUpdatesViewed = true ) ) } @Test - fun disabledInstalledAppDoesNotMarkNewVersionAsUpdate() { + fun newVersionOfSleepingInstalledAppMarksUpdate() { + // Enabled state is not a sync input: disabled apps are notified and hidden from update UI. assertEquals( - AppUpdateDecision.DISABLED_INSTALLED, + AppUpdateDecision.MARK_UPDATED, selectAppUpdateDecision( remoteVersion = 101, cachedVersion = 100, installedVersion = 100, status = App.STATUS_NORMAL, - lastUpdatesViewed = false, - installedEnabled = false + lastUpdatesViewed = false ) ) } @Test - fun disabledInstalledAppClearsExistingUpdate() { + fun markedSleepingAppUpdateIsKeptOnNextSyncWithoutRepeatNotification() { assertEquals( - AppUpdateDecision.CLEAR_DISABLED_UPDATE, + AppUpdateDecision.KEEP_DEVICE_UPDATE, selectAppUpdateDecision( remoteVersion = 101, cachedVersion = 101, installedVersion = 100, status = App.STATUS_UPDATED, - lastUpdatesViewed = false, - installedEnabled = false + lastUpdatesViewed = true + ) + ) + } + + @Test + fun previouslySuppressedSleepingAppUpdateIsRestoredWithoutNotification() { + assertEquals( + AppUpdateDecision.RESTORE_DEVICE_UPDATE, + selectAppUpdateDecision( + remoteVersion = 101, + cachedVersion = 101, + installedVersion = 100, + status = App.STATUS_NORMAL, + lastUpdatesViewed = false ) ) } @@ -194,8 +200,7 @@ class UpdateCheckVersionRollbackTest { cachedVersion = 100, installedVersion = 100, status = App.STATUS_NORMAL, - lastUpdatesViewed = false, - installedEnabled = true + lastUpdatesViewed = false ) ) } @@ -209,8 +214,7 @@ class UpdateCheckVersionRollbackTest { cachedVersion = 100, installedVersion = 0, status = App.STATUS_NORMAL, - lastUpdatesViewed = false, - installedEnabled = false + lastUpdatesViewed = false ) ) } diff --git a/app/src/test/java/com/anod/appwatcher/utils/SyncNotificationFilterTest.kt b/app/src/test/java/com/anod/appwatcher/utils/SyncNotificationFilterTest.kt index 1c59bdba..47bd1d21 100644 --- a/app/src/test/java/com/anod/appwatcher/utils/SyncNotificationFilterTest.kt +++ b/app/src/test/java/com/anod/appwatcher/utils/SyncNotificationFilterTest.kt @@ -94,11 +94,27 @@ class SyncNotificationFilterTest { ), actual) } + @Test + fun disabledInstalledAppIsNotifiedWithoutDeviceUpdate() { + val filter = SyncNotification.Filter( + filterInstalled = false, + filterInstalledUpToDate = true, + filterNoChanges = false) + val disabled = updatedApp(1, installedVersionCode = 1, versionNumber = 2, installedEnabled = false) + val enabled = updatedApp(2, installedVersionCode = 1, versionNumber = 2, installedEnabled = true) + + assertEquals(listOf(disabled, enabled), filter.apply(listOf(disabled, enabled))) + assertFalse(disabled.canUpdateOnDevice) + assertTrue(enabled.canUpdateOnDevice) + assertFalse(updatedApp(3, installedVersionCode = 0, installedEnabled = true).canUpdateOnDevice) + } + private fun updatedApp( id: Int, installedVersionCode: Int = 0, versionNumber: Int = 0, - noNewDetails: Boolean = false + noNewDetails: Boolean = false, + installedEnabled: Boolean = installedVersionCode > 0 ) = UpdatedApp( packageName = "item-$id", versionNumber = versionNumber, @@ -107,6 +123,7 @@ class SyncNotificationFilterTest { uploadDate = "100", recentChanges = "", installedVersionCode = installedVersionCode, + installedEnabled = installedEnabled, isNewUpdate = false, noNewDetails = noNewDetails ) diff --git a/app/src/test/java/com/anod/appwatcher/watchlist/WatchListPagingSourceRoomTest.kt b/app/src/test/java/com/anod/appwatcher/watchlist/WatchListPagingSourceRoomTest.kt index 938625b4..9fb36dd9 100644 --- a/app/src/test/java/com/anod/appwatcher/watchlist/WatchListPagingSourceRoomTest.kt +++ b/app/src/test/java/com/anod/appwatcher/watchlist/WatchListPagingSourceRoomTest.kt @@ -14,8 +14,6 @@ import com.anod.appwatcher.model.Filters import com.anod.appwatcher.preferences.Preferences import com.anod.appwatcher.utils.PackageState import com.anod.appwatcher.utils.PackageStateProvider -import com.anod.appwatcher.utils.clearDisabledUpdateStatus -import com.anod.appwatcher.utils.clearDisabledUpdateStatuses import info.anodsplace.framework.content.InstalledApps import info.anodsplace.notification.NotificationManager import kotlinx.coroutines.CoroutineScope @@ -140,7 +138,7 @@ class WatchListPagingSourceRoomTest { } @Test - fun disabledUpdateStatusIsClearedBeforePackageIsReenabled() = runBlocking { + fun sleepingAppUpdateIsKeptButShownAsDisabledUntilReenabled() = runBlocking { insertApp( appId = "disabled", packageName = "disabled.watched", @@ -155,73 +153,61 @@ class WatchListPagingSourceRoomTest { ) ) - val cleared = db.apps().clearDisabledUpdateStatuses( - packageStates = packageStateProvider( - installedApps = installedApps, - packageEnabled = { false } + val disabledItem = loadSingleApp( + createPagingSource( + showOnDevice = false, + showRecentlyDiscovered = true, + packageEnabled = { false }, + installedApps = installedApps ) ) - assertEquals(1, cleared) - val storedApp = db.apps().loadApp("disabled")!! - assertEquals(App.STATUS_NORMAL, storedApp.status) - assertEquals(0L, storedApp.syncTime) - - val result = createPagingSource( - showOnDevice = false, - packageEnabled = { true }, - installedApps = installedApps - ).load(PagingSource.LoadParams.Refresh(key = null, loadSize = 20, placeholdersEnabled = false)) - val appItem = (result as PagingSource.LoadResult.Page) - .data - .filterIsInstance() - .single() - - assertEquals(App.STATUS_NORMAL, appItem.appListItem.app.status) + assertFalse(disabledItem.isPackageEnabled) + assertEquals(App.STATUS_UPDATED, disabledItem.appListItem.app.status) assertEquals( SectionHeader.Watching, - DefaultSectionHeaderFactory(showRecentlyDiscovered = false) - .insertSeparator(before = null, after = appItem) + DefaultSectionHeaderFactory(showRecentlyDiscovered = true) + .insertSeparator(before = null, after = disabledItem) ?.type ) - } - - @Test - fun packageChangeClearsOnlyChangedDisabledUpdateStatus() = runBlocking { - insertApp( - appId = "disabled", - packageName = "disabled.watched", - title = "Disabled Watched", - versionNumber = 2, - status = App.STATUS_UPDATED, - syncTime = System.currentTimeMillis() - ) - insertApp( - appId = "other", - packageName = "other.watched", - title = "Other Watched", - versionNumber = 2, - status = App.STATUS_UPDATED, - syncTime = System.currentTimeMillis() - ) - val installedApps = InstalledApps.StaticMap( - mapOf( - "disabled.watched" to InstalledApps.Info(versionCode = 1, versionName = "1"), - "other.watched" to InstalledApps.Info(versionCode = 1, versionName = "1") + assertTrue( + loadApps( + createPagingSource( + showOnDevice = false, + filterId = Filters.UPDATABLE, + packageEnabled = { false }, + installedApps = installedApps + ) + ).isEmpty() + ) + assertEquals(App.STATUS_UPDATED, db.apps().loadApp("disabled")!!.status) + + val enabledItem = loadSingleApp( + createPagingSource( + showOnDevice = false, + showRecentlyDiscovered = true, + packageEnabled = { true }, + installedApps = installedApps ) ) - val cleared = db.apps().clearDisabledUpdateStatus( - packageName = "disabled.watched", - packageState = PackageState( - packageInfo = installedApps.packageInfo("disabled.watched"), - isEnabled = false - ) + assertEquals( + SectionHeader.New, + DefaultSectionHeaderFactory(showRecentlyDiscovered = true) + .insertSeparator(before = null, after = enabledItem) + ?.type + ) + assertEquals( + listOf("disabled.watched"), + loadApps( + createPagingSource( + showOnDevice = false, + filterId = Filters.UPDATABLE, + packageEnabled = { true }, + installedApps = installedApps + ) + ).map { it.appListItem.app.packageName } ) - - assertEquals(1, cleared) - assertEquals(App.STATUS_NORMAL, db.apps().loadApp("disabled")!!.status) - assertEquals(App.STATUS_UPDATED, db.apps().loadApp("other")!!.status) } @Test @@ -548,6 +534,14 @@ class WatchListPagingSourceRoomTest { packageStates = packageStateProvider(installedApps, packageEnabled) ) + private suspend fun loadApps(pagingSource: WatchListPagingSource): List = + (pagingSource.load(PagingSource.LoadParams.Refresh(key = null, loadSize = 20, placeholdersEnabled = false)) as PagingSource.LoadResult.Page) + .data + .filterIsInstance() + + private suspend fun loadSingleApp(pagingSource: WatchListPagingSource): SectionItem.App = + loadApps(pagingSource).single() + private fun packageStateProvider( installedApps: InstalledApps, packageEnabled: (String) -> Boolean