diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index f4f0988ec2c093f2ff53ebf9ede1caad0aea552d..46e48eafb2f04d1d997a04cea47872364444b279 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -125,6 +125,7 @@ class MainActivity : AppCompatActivity() { viewModel.updateAppWarningList() viewModel.updateContentRatings() + viewModel.fetchUpdatableSystemAppsList() observeEvents() diff --git a/app/src/main/java/foundation/e/apps/data/application/downloadInfo/DownloadInfoApiImpl.kt b/app/src/main/java/foundation/e/apps/data/application/downloadInfo/DownloadInfoApiImpl.kt index a0e47ae3f5d83c9b5311c3e3ff99954eb70c2c30..86b91123092891b22fbd9502b27559c075dda5d6 100644 --- a/app/src/main/java/foundation/e/apps/data/application/downloadInfo/DownloadInfoApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/application/downloadInfo/DownloadInfoApiImpl.kt @@ -70,7 +70,7 @@ class DownloadInfoApiImpl @Inject constructor( } Origin.GITLAB_RELEASES -> { - // TODO + return // nothing to do as downloadURLList is already set } } diff --git a/app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt b/app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt index 04812c24ad43ebed3552b95e8f8eba144c07aca1..25d9487da644fff2e6420c06914223fad997bc7f 100644 --- a/app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/gitlab/SystemAppsUpdatesRepository.kt @@ -43,8 +43,15 @@ class SystemAppsUpdatesRepository @Inject constructor( private var systemAppProjectList = mutableListOf() - suspend fun fetchUpdatableSystemApps() { + private fun getUpdatableSystemApps(): List { + return systemAppProjectList.map { it.packageName } + } + + suspend fun fetchUpdatableSystemApps(forceRefresh: Boolean = false) { val result = handleNetworkResult { + if (getUpdatableSystemApps().isNotEmpty() && !forceRefresh) { + return@handleNetworkResult + } val response = updatableSystemAppsApi.getUpdatableSystemApps() if (response.isSuccessful && !response.body().isNullOrEmpty()) { response.body()?.let { systemAppProjectList.addAll(it) } @@ -58,10 +65,6 @@ class SystemAppsUpdatesRepository @Inject constructor( } } - fun getUpdatableSystemApps(): List { - return systemAppProjectList.map { it.packageName } - } - private fun isSystemAppBlocked( systemAppInfo: SystemAppInfo, sdkLevel: Int, diff --git a/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt b/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt index 7cc60ac310ec918f13b523df39f22d97e43146dc..ddaacfbadf28ba822c928cd5c34090b6e87fe5d8 100644 --- a/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt @@ -34,6 +34,7 @@ import foundation.e.apps.data.playstore.PlayStoreRepositoryImpl import foundation.e.apps.data.application.ApplicationRepository import foundation.e.apps.data.application.search.SearchApi.Companion.APP_TYPE_ANY import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.gitlab.SystemAppsUpdatesRepository import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.data.preference.AppLoungePreference import foundation.e.apps.install.pkg.AppLoungePackageManager @@ -53,6 +54,7 @@ class UpdatesManagerImpl @Inject constructor( private val appLoungePreference: AppLoungePreference, private val fdroidRepository: FdroidRepository, private val blockedAppRepository: BlockedAppRepository, + private val systemAppsUpdatesRepository: SystemAppsUpdatesRepository, ) { companion object { @@ -123,8 +125,12 @@ class UpdatesManagerImpl @Inject constructor( status = if (status == ResultStatus.OK) status else gplayStatus } + val systemApps = getSystemAppUpdates() val nonFaultyUpdateList = faultyAppRepository.removeFaultyApps(updateList) - return Pair(nonFaultyUpdateList, status) + + addSystemAppsAtFirst(updateList, nonFaultyUpdateList, systemApps) + + return Pair(updateList, status) } suspend fun getUpdatesOSS(): Pair, ResultStatus> { @@ -157,8 +163,35 @@ class UpdatesManagerImpl @Inject constructor( }, updateList) } + val systemApps = getSystemAppUpdates() val nonFaultyUpdateList = faultyAppRepository.removeFaultyApps(updateList) - return Pair(nonFaultyUpdateList, status) + + addSystemAppsAtFirst(updateList, nonFaultyUpdateList, systemApps) + + return Pair(updateList, status) + } + + private suspend fun getSystemAppUpdates(): List { + val systemApps = mutableListOf() + getUpdatesFromApi({ + Pair(systemAppsUpdatesRepository.getSystemUpdates(), ResultStatus.OK) + }, systemApps) + return systemApps + } + + /** + * This method adds the system app updates at the beginning of the update list. + * It will ensure our system apps are updated first, followed by other apps, + * avoiding potential conflicts. + */ + private fun addSystemAppsAtFirst( + updateList: MutableList, + nonFaultyApps: List, + systemApps: List, + ) { + updateList.clear() + updateList.addAll(systemApps) + updateList.addAll(nonFaultyApps) } /** diff --git a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorkManager.kt b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorkManager.kt index fa41365edc1f4379d44ce68aa7fb68a0db1c4803..9cac3976926777960bcd3cb45c8f72645c1418db 100644 --- a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorkManager.kt +++ b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorkManager.kt @@ -47,8 +47,11 @@ object UpdatesWorkManager { return OneTimeWorkRequest.Builder(UpdatesWorker::class.java).apply { setConstraints(buildWorkerConstraints()) addTag(USER_TAG) - }.setInputData(Data.Builder().putBoolean(UpdatesWorker.IS_AUTO_UPDATE, false).build()) - .build() + }.setInputData( + Data.Builder() + .putBoolean(UpdatesWorker.IS_AUTO_UPDATE, false) + .build() + ).build() } private fun buildWorkerConstraints() = Constraints.Builder().apply { diff --git a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt index ee181a81a655bce1ace25a6cb5f385021d8879ca..65f78637e16d61cfe75b828f6e4e683ea286f023 100644 --- a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt +++ b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt @@ -20,6 +20,7 @@ import foundation.e.apps.data.blockedApps.BlockedAppRepository import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.User import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.gitlab.SystemAppsUpdatesRepository import foundation.e.apps.data.login.AuthenticatorRepository import foundation.e.apps.data.preference.DataStoreManager import foundation.e.apps.data.updates.UpdatesManagerRepository @@ -40,6 +41,7 @@ class UpdatesWorker @AssistedInject constructor( private val authenticatorRepository: AuthenticatorRepository, private val appInstallProcessor: AppInstallProcessor, private val blockedAppRepository: BlockedAppRepository, + private val systemAppsUpdatesRepository: SystemAppsUpdatesRepository, ) : CoroutineWorker(context, params) { companion object { @@ -62,6 +64,7 @@ class UpdatesWorker @AssistedInject constructor( } refreshBlockedAppList() + systemAppsUpdatesRepository.fetchUpdatableSystemApps(forceRefresh = true) checkForUpdates() Result.success() } catch (e: Throwable) { diff --git a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt index 83d9d7e940f1f48b3dc58ecf854d6929fb3f8691..505b95852ea506a4946076221b68a936c6e9d29e 100644 --- a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt +++ b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt @@ -29,6 +29,7 @@ import foundation.e.apps.data.enums.Type import foundation.e.apps.data.application.ApplicationRepository import foundation.e.apps.data.application.UpdatesDao import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.enums.Origin import foundation.e.apps.data.install.models.AppInstall import foundation.e.apps.data.playstore.utils.GplayHttpRequestException import foundation.e.apps.data.preference.DataStoreManager @@ -97,7 +98,7 @@ class AppInstallProcessor @Inject constructor( it.contentRating = application.contentRating } - if (appInstall.type == Type.PWA) { + if (appInstall.type == Type.PWA || application.origin == Origin.GITLAB_RELEASES) { appInstall.downloadURLList = mutableListOf(application.url) } diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt index 474308feee5eb73de4f517a430c8a8f950865c0e..cd54ddf68e06ad1ae8e0272b1cbd50b63d96355c 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt @@ -39,6 +39,7 @@ import foundation.e.apps.data.ecloud.EcloudRepository import foundation.e.apps.data.enums.User import foundation.e.apps.data.enums.isInitialized import foundation.e.apps.data.enums.isUnFiltered +import foundation.e.apps.data.gitlab.SystemAppsUpdatesRepository import foundation.e.apps.data.install.AppManagerWrapper import foundation.e.apps.data.install.models.AppInstall import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository @@ -64,6 +65,7 @@ class MainActivityViewModel @Inject constructor( private val gPlayContentRatingRepository: GPlayContentRatingRepository, private val fDroidAntiFeatureRepository: FDroidAntiFeatureRepository, private val appInstallProcessor: AppInstallProcessor, + private val systemAppsUpdatesRepository: SystemAppsUpdatesRepository, ) : ViewModel() { val tocStatus: LiveData = appLoungeDataStore.tocStatus.asLiveData() @@ -247,6 +249,12 @@ class MainActivityViewModel @Inject constructor( } } + fun fetchUpdatableSystemAppsList() { + viewModelScope.launch { + systemAppsUpdatesRepository.fetchUpdatableSystemApps() + } + } + fun getAppNameByPackageName(packageName: String): String { return appLoungePackageManager.getAppNameFromPackageName(packageName) } diff --git a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt index 214e3ee21b47515b8abc0a11bfc0999feaa563ee..d483c05c5fb2a699a4edc3b7399e0891fa7a8ca4 100644 --- a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt +++ b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt @@ -30,6 +30,7 @@ import foundation.e.apps.data.fdroid.FdroidRepository import foundation.e.apps.data.application.ApplicationRepository import foundation.e.apps.data.application.search.SearchApi import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.gitlab.SystemAppsUpdatesRepository import foundation.e.apps.data.updates.UpdatesManagerImpl import foundation.e.apps.util.MainCoroutineRule import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -78,6 +79,9 @@ class UpdateManagerImptTest { @Mock private lateinit var fdroidRepository: FdroidRepository + @Mock + private lateinit var systemAppsUpdatesRepository: SystemAppsUpdatesRepository + val authData = AuthData("e@e.email", "AtadyMsIAtadyM") @Before @@ -93,27 +97,40 @@ class UpdateManagerImptTest { faultyAppRepository, preferenceModule, fdroidRepository, - blockedAppRepository + blockedAppRepository, + systemAppsUpdatesRepository, ) } + private fun getSystemApps(status: Status = Status.UPDATABLE) = mutableListOf( + Application( + status = status, + name = "Demo Four", + package_name = "foundation.e.demofour", + origin = Origin.GITLAB_RELEASES, + filterLevel = FilterLevel.NONE + ) + ) + @Test fun getUpdateWhenUpdateIsAvailable() = runTest { val gplayApps = getGplayApps() val openSourceApps = getOpenSourceApps(Status.UPDATABLE) + val systemAppUpdates = getSystemApps() val openSourceUpdates = Pair(openSourceApps, ResultStatus.OK) val gplayUpdates = Pair(gplayApps, ResultStatus.OK) setupMockingForFetchingUpdates( openSourceUpdates, - gplayUpdates + gplayUpdates, + systemAppUpdates ) val updateResult = updatesManagerImpl.getUpdates(authData) System.out.println("===> updates: ${updateResult.first.map { it.package_name }}") - assertEquals("fetchUpdate", 2, updateResult.first.size) + assertEquals("fetchUpdate", 3, updateResult.first.size) } private fun getGplayApps(status: Status = Status.UPDATABLE) = mutableListOf( @@ -140,6 +157,8 @@ class UpdateManagerImptTest { val authData = AuthData("e@e.email", "AtadyMsIAtadyM") pkgManagerModule.applicationInfo.clear() + setupMockingSystemApps() + val updateResult = updatesManagerImpl.getUpdates(authData) System.out.println("===> updates: ${updateResult.first.map { it.package_name }}") @@ -150,13 +169,15 @@ class UpdateManagerImptTest { fun getUpdateWhenUpdateIsUnavailable() = runTest { val gplayApps = getGplayApps(Status.INSTALLED) val openSourceApps = getOpenSourceApps(Status.INSTALLED) + val systemAppUpdates = getSystemApps(Status.INSTALLED) val openSourceUpdates = Pair(openSourceApps, ResultStatus.OK) val gplayUpdates = Pair(gplayApps, ResultStatus.OK) setupMockingForFetchingUpdates( openSourceUpdates, - gplayUpdates + gplayUpdates, + systemAppUpdates, ) val updateResult = updatesManagerImpl.getUpdates(authData) @@ -169,36 +190,59 @@ class UpdateManagerImptTest { fun getUpdateWhenUpdateHasOnlyForOpenSourceApps() = runTest { val gplayApps = getGplayApps(Status.INSTALLED) val openSourceApps = getOpenSourceApps(Status.UPDATABLE) + val systemAppUpdates = getSystemApps(Status.INSTALLED) val openSourceUpdates = Pair(openSourceApps, ResultStatus.OK) val gplayUpdates = Pair(gplayApps, ResultStatus.OK) setupMockingForFetchingUpdates( openSourceUpdates, - gplayUpdates + gplayUpdates, + systemAppUpdates, ) val updateResult = updatesManagerImpl.getUpdates(authData) System.out.println("===> updates: ${updateResult.first.map { it.package_name }}") - assertFalse("fetchupdate", updateResult.first.any { it.origin == Origin.GPLAY }) + assertFalse("fetchupdate", updateResult.first.any { it.origin != Origin.CLEANAPK }) } @Test fun getUpdateWhenUpdateHasOnlyForGplayApps() = runTest { val gplayApps = getGplayApps(Status.UPDATABLE) val openSourceApps = getOpenSourceApps(Status.INSTALLED) + val systemAppUpdates = getSystemApps(Status.INSTALLED) val openSourceUpdates = Pair(openSourceApps, ResultStatus.OK) val gplayUpdates = Pair(gplayApps, ResultStatus.OK) setupMockingForFetchingUpdates( openSourceUpdates, - gplayUpdates + gplayUpdates, + systemAppUpdates, + ) + + val updateResult = updatesManagerImpl.getUpdates(authData) + assertFalse("fetchupdate", updateResult.first.any { it.origin != Origin.GPLAY }) + } + + @Test + fun getUpdateWhenUpdateHasOnlySystemApps() = runTest { + val gplayApps = getGplayApps(Status.INSTALLED) + val openSourceApps = getOpenSourceApps(Status.INSTALLED) + val systemAppUpdates = getSystemApps(Status.UPDATABLE) + + val openSourceUpdates = Pair(openSourceApps, ResultStatus.OK) + val gplayUpdates = Pair(gplayApps, ResultStatus.OK) + + setupMockingForFetchingUpdates( + openSourceUpdates, + gplayUpdates, + systemAppUpdates, ) val updateResult = updatesManagerImpl.getUpdates(authData) - assertFalse("fetchupdate", updateResult.first.any { it.origin == Origin.CLEANAPK }) + assertFalse("fetchupdate", updateResult.first.any { it.origin != Origin.GITLAB_RELEASES }) } @Test @@ -274,15 +318,17 @@ class UpdateManagerImptTest { fun getUpdatesOSSWhenUpdateIsAvailable() = runTest { val openSourceApps = getOpenSourceApps(Status.UPDATABLE) val gPlayApps = getGplayApps(Status.UPDATABLE) + val systemAppUpdates = getSystemApps() val openSourceUpdates = Pair(openSourceApps, ResultStatus.OK) val gplayUpdates = Pair(gPlayApps, ResultStatus.OK) - setupMockingForFetchingUpdates(openSourceUpdates, gplayUpdates) + setupMockingForFetchingUpdates(openSourceUpdates, gplayUpdates, systemAppUpdates) val updateResult = updatesManagerImpl.getUpdatesOSS() - assertEquals("UpdateOSS", 1, updateResult.first.size) - assertEquals("UpdateOSS", Origin.CLEANAPK, updateResult.first[0].origin) + assertEquals("UpdateOSS", 2, updateResult.first.size) + assertEquals("UpdateOSS", Origin.CLEANAPK, updateResult.first[1].origin) + assertEquals("UpdateOSS", Origin.GITLAB_RELEASES, updateResult.first[0].origin) } @Test @@ -317,6 +363,7 @@ class UpdateManagerImptTest { private suspend fun setupMockingForFetchingUpdates( openSourceUpdates: Pair, ResultStatus>, gplayUpdates: Pair, ResultStatus>, + systemAppUpdates: MutableList = mutableListOf(), selectedApplicationSources: List = mutableListOf( SearchApi.APP_TYPE_ANY, SearchApi.APP_TYPE_OPEN, @@ -334,6 +381,8 @@ class UpdateManagerImptTest { Mockito.`when`(applicationRepository.getSelectedAppTypes()) .thenReturn(selectedApplicationSources) + setupMockingSystemApps(systemAppUpdates) + if (gplayUpdates.first.isNotEmpty()) { Mockito.`when`( applicationRepository.getApplicationDetails( @@ -357,4 +406,11 @@ class UpdateManagerImptTest { ).thenReturn(Pair(Application(), ResultStatus.TIMEOUT)) } } + + private suspend fun setupMockingSystemApps( + systemAppUpdates: MutableList = mutableListOf() + ) { + Mockito.`when`(systemAppsUpdatesRepository.getSystemUpdates()) + .thenReturn(systemAppUpdates) + } }