Loading app/src/main/java/foundation/e/apps/data/di/bindings/LoginBindingModule.kt 0 → 100644 +60 −0 Original line number Diff line number Diff line /* * Copyright (C) 2026 e Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.di.bindings import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import foundation.e.apps.data.login.microg.MicrogLoginManager import foundation.e.apps.data.login.repository.AppLoginRepository import foundation.e.apps.data.login.repository.AuthenticatorRepository import foundation.e.apps.domain.login.LoginRepository import foundation.e.apps.login.MicrogAccountFetcher import foundation.e.apps.login.PlayStoreAuthManager import foundation.e.apps.login.StoreAuthCoordinator import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) interface LoginBindingModule { @Binds @Singleton fun bindLoginRepository( appLoginRepository: AppLoginRepository, ): LoginRepository @Binds @Singleton fun bindPlayStoreAuthManager( authenticatorRepository: AuthenticatorRepository, ): PlayStoreAuthManager @Binds @Singleton fun bindStoreAuthCoordinator( authenticatorRepository: AuthenticatorRepository, ): StoreAuthCoordinator @Binds @Singleton fun bindMicrogAccountFetcher( microgLoginManager: MicrogLoginManager, ): MicrogAccountFetcher } app/src/main/java/foundation/e/apps/data/install/updates/UpdatesCoordinator.kt 0 → 100644 +56 −0 Original line number Diff line number Diff line /* * Copyright (C) 2026 e Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.install.updates import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.blockedApps.BlockedAppRepository import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.gitlab.SystemAppsUpdatesRepository import foundation.e.apps.data.updates.UpdatesManagerRepository import foundation.e.apps.domain.model.User import javax.inject.Inject class UpdatesCoordinator @Inject constructor( private val updatesManagerRepository: UpdatesManagerRepository, private val blockedAppRepository: BlockedAppRepository, private val systemAppsUpdatesRepository: SystemAppsUpdatesRepository, ) { suspend fun refreshBlockedAppWarnings(): Boolean { return blockedAppRepository.fetchUpdateOfAppWarningList() } suspend fun refreshSystemApps(): ResultSupreme<Unit> { return systemAppsUpdatesRepository.fetchUpdatableSystemApps(forceRefresh = true) } suspend fun getAvailableUpdates( user: User, hasAuthenticatedPlayStoreSession: Boolean, ): Pair<List<Application>, ResultStatus> { val canUsePlayStoreUpdates = (user == User.ANONYMOUS || user == User.GOOGLE) && hasAuthenticatedPlayStoreSession return if (canUsePlayStoreUpdates) { updatesManagerRepository.getUpdates() } else if (user == User.NO_GOOGLE) { updatesManagerRepository.getUpdatesOSS() } else { Pair(emptyList(), ResultStatus.UNKNOWN) } } } app/src/main/java/foundation/e/apps/data/install/updates/UpdatesWorker.kt +18 −17 Original line number Diff line number Diff line Loading @@ -15,34 +15,28 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.blockedApps.BlockedAppRepository import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.event.AppEvent import foundation.e.apps.data.event.EventBus import foundation.e.apps.data.gitlab.SystemAppsUpdatesRepository import foundation.e.apps.data.install.workmanager.AppInstallProcessor import foundation.e.apps.data.login.repository.AuthenticatorRepository import foundation.e.apps.data.updates.UpdatesManagerRepository import foundation.e.apps.domain.model.User import foundation.e.apps.domain.preferences.AppPreferencesRepository import foundation.e.apps.domain.preferences.SessionRepository import foundation.e.apps.login.PlayStoreAuthManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext import timber.log.Timber @Suppress("LongParameterList") @HiltWorker class UpdatesWorker @AssistedInject constructor( @Assisted private val context: Context, @Assisted private val params: WorkerParameters, private val updatesManagerRepository: UpdatesManagerRepository, private val updatesCoordinator: UpdatesCoordinator, private val sessionRepository: SessionRepository, private val appPreferencesRepository: AppPreferencesRepository, private val authenticatorRepository: AuthenticatorRepository, private val playStoreAuthManager: PlayStoreAuthManager, private val appInstallProcessor: AppInstallProcessor, private val blockedAppRepository: BlockedAppRepository, private val systemAppsUpdatesRepository: SystemAppsUpdatesRepository, ) : CoroutineWorker(context, params) { companion object { Loading @@ -67,13 +61,12 @@ class UpdatesWorker @AssistedInject constructor( } if (isAutoUpdate) { check(blockedAppRepository.fetchUpdateOfAppWarningList()) { check(updatesCoordinator.refreshBlockedAppWarnings()) { "failed to update app blocklist" } } val systemAppsUpdateTask = systemAppsUpdatesRepository.fetchUpdatableSystemApps(forceRefresh = true) val systemAppsUpdateTask = updatesCoordinator.refreshSystemApps() check(systemAppsUpdateTask.isSuccess()) { "failed to fetch system apps update!" } val enqueueInstall = checkForUpdates() check(enqueueInstall == ResultStatus.OK) { Loading Loading @@ -156,7 +149,7 @@ class UpdatesWorker @AssistedInject constructor( loadSettings() val appsNeededToUpdate = mutableListOf<Application>() val user = getUser() val authData = authenticatorRepository.getValidatedAuthData().data val authData = playStoreAuthManager.getValidatedAuthData().data val resultStatus: ResultStatus if (user in listOf(User.ANONYMOUS, User.GOOGLE) && authData != null) { Loading @@ -165,14 +158,20 @@ class UpdatesWorker @AssistedInject constructor( * apps from Google Play store. * The user check will be more useful in No-Google mode. */ val (apps, status) = updatesManagerRepository.getUpdates() val (apps, status) = updatesCoordinator.getAvailableUpdates( user = user, hasAuthenticatedPlayStoreSession = true ) appsNeededToUpdate.addAll(apps) resultStatus = status } else if (user == User.NO_GOOGLE) { /* * If authData is null, update apps from cleanapk only. */ val (apps, status) = updatesManagerRepository.getUpdatesOSS() val (apps, status) = updatesCoordinator.getAvailableUpdates( user = user, hasAuthenticatedPlayStoreSession = false ) appsNeededToUpdate.addAll(apps) resultStatus = status } else { Loading Loading @@ -224,9 +223,11 @@ class UpdatesWorker @AssistedInject constructor( // returns list of Pair(app, status(success|failed)) @VisibleForTesting suspend fun startUpdateProcess(appsNeededToUpdate: List<Application>): List<Pair<Application, Boolean>> { suspend fun startUpdateProcess( appsNeededToUpdate: List<Application> ): List<Pair<Application, Boolean>> { val response = mutableListOf<Pair<Application, Boolean>>() val authData = authenticatorRepository.getValidatedAuthData() val authData = playStoreAuthManager.getValidatedAuthData() val isNotLoggedIntoPersonalAccount = !authData.isValidData() || authData.data?.isAnonymous == true for (fusedApp in appsNeededToUpdate) { Loading app/src/main/java/foundation/e/apps/data/install/workmanager/InstallWorkManager.kt +0 −4 Original line number Diff line number Diff line Loading @@ -37,10 +37,6 @@ object InstallWorkManager { return operation } fun cancelWork(context: Context, tag: String) { WorkManager.getInstance(context).cancelAllWorkByTag(tag) } fun checkWorkIsAlreadyAvailable(context: Context, tag: String): Boolean { val works = WorkManager.getInstance(context).getWorkInfosByTag(tag) try { Loading app/src/main/java/foundation/e/apps/data/login/cleanapk/CleanApkAuthenticator.kt +2 −6 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import foundation.e.apps.data.login.core.StoreAuthResult import foundation.e.apps.data.login.core.StoreAuthenticator import foundation.e.apps.data.login.core.StoreType import foundation.e.apps.domain.model.LoginState import foundation.e.apps.domain.model.User import foundation.e.apps.domain.preferences.AppPreferencesRepository import foundation.e.apps.domain.preferences.SessionRepository import javax.inject.Inject Loading @@ -40,11 +39,8 @@ class CleanApkAuthenticator @Inject constructor( ) : StoreAuthenticator { override val storeType: StoreType = StoreType.CLEAN_APK private val user: User get() = sessionRepository.getUser() override fun isStoreActive(): Boolean { if (sessionRepository.getLoginState() == LoginState.UNAVAILABLE) { if (sessionRepository.loginState.value == LoginState.UNAVAILABLE) { /* * UNAVAILABLE login state means first login is not completed. */ Loading @@ -57,7 +53,7 @@ class CleanApkAuthenticator @Inject constructor( return StoreAuthResult( authObject = AuthObject.CleanApk( ResultSupreme.Success(Unit), user, sessionRepository.awaitUser(), ) ) } Loading Loading
app/src/main/java/foundation/e/apps/data/di/bindings/LoginBindingModule.kt 0 → 100644 +60 −0 Original line number Diff line number Diff line /* * Copyright (C) 2026 e Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.di.bindings import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import foundation.e.apps.data.login.microg.MicrogLoginManager import foundation.e.apps.data.login.repository.AppLoginRepository import foundation.e.apps.data.login.repository.AuthenticatorRepository import foundation.e.apps.domain.login.LoginRepository import foundation.e.apps.login.MicrogAccountFetcher import foundation.e.apps.login.PlayStoreAuthManager import foundation.e.apps.login.StoreAuthCoordinator import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) interface LoginBindingModule { @Binds @Singleton fun bindLoginRepository( appLoginRepository: AppLoginRepository, ): LoginRepository @Binds @Singleton fun bindPlayStoreAuthManager( authenticatorRepository: AuthenticatorRepository, ): PlayStoreAuthManager @Binds @Singleton fun bindStoreAuthCoordinator( authenticatorRepository: AuthenticatorRepository, ): StoreAuthCoordinator @Binds @Singleton fun bindMicrogAccountFetcher( microgLoginManager: MicrogLoginManager, ): MicrogAccountFetcher }
app/src/main/java/foundation/e/apps/data/install/updates/UpdatesCoordinator.kt 0 → 100644 +56 −0 Original line number Diff line number Diff line /* * Copyright (C) 2026 e Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.install.updates import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.blockedApps.BlockedAppRepository import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.gitlab.SystemAppsUpdatesRepository import foundation.e.apps.data.updates.UpdatesManagerRepository import foundation.e.apps.domain.model.User import javax.inject.Inject class UpdatesCoordinator @Inject constructor( private val updatesManagerRepository: UpdatesManagerRepository, private val blockedAppRepository: BlockedAppRepository, private val systemAppsUpdatesRepository: SystemAppsUpdatesRepository, ) { suspend fun refreshBlockedAppWarnings(): Boolean { return blockedAppRepository.fetchUpdateOfAppWarningList() } suspend fun refreshSystemApps(): ResultSupreme<Unit> { return systemAppsUpdatesRepository.fetchUpdatableSystemApps(forceRefresh = true) } suspend fun getAvailableUpdates( user: User, hasAuthenticatedPlayStoreSession: Boolean, ): Pair<List<Application>, ResultStatus> { val canUsePlayStoreUpdates = (user == User.ANONYMOUS || user == User.GOOGLE) && hasAuthenticatedPlayStoreSession return if (canUsePlayStoreUpdates) { updatesManagerRepository.getUpdates() } else if (user == User.NO_GOOGLE) { updatesManagerRepository.getUpdatesOSS() } else { Pair(emptyList(), ResultStatus.UNKNOWN) } } }
app/src/main/java/foundation/e/apps/data/install/updates/UpdatesWorker.kt +18 −17 Original line number Diff line number Diff line Loading @@ -15,34 +15,28 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.blockedApps.BlockedAppRepository import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.event.AppEvent import foundation.e.apps.data.event.EventBus import foundation.e.apps.data.gitlab.SystemAppsUpdatesRepository import foundation.e.apps.data.install.workmanager.AppInstallProcessor import foundation.e.apps.data.login.repository.AuthenticatorRepository import foundation.e.apps.data.updates.UpdatesManagerRepository import foundation.e.apps.domain.model.User import foundation.e.apps.domain.preferences.AppPreferencesRepository import foundation.e.apps.domain.preferences.SessionRepository import foundation.e.apps.login.PlayStoreAuthManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext import timber.log.Timber @Suppress("LongParameterList") @HiltWorker class UpdatesWorker @AssistedInject constructor( @Assisted private val context: Context, @Assisted private val params: WorkerParameters, private val updatesManagerRepository: UpdatesManagerRepository, private val updatesCoordinator: UpdatesCoordinator, private val sessionRepository: SessionRepository, private val appPreferencesRepository: AppPreferencesRepository, private val authenticatorRepository: AuthenticatorRepository, private val playStoreAuthManager: PlayStoreAuthManager, private val appInstallProcessor: AppInstallProcessor, private val blockedAppRepository: BlockedAppRepository, private val systemAppsUpdatesRepository: SystemAppsUpdatesRepository, ) : CoroutineWorker(context, params) { companion object { Loading @@ -67,13 +61,12 @@ class UpdatesWorker @AssistedInject constructor( } if (isAutoUpdate) { check(blockedAppRepository.fetchUpdateOfAppWarningList()) { check(updatesCoordinator.refreshBlockedAppWarnings()) { "failed to update app blocklist" } } val systemAppsUpdateTask = systemAppsUpdatesRepository.fetchUpdatableSystemApps(forceRefresh = true) val systemAppsUpdateTask = updatesCoordinator.refreshSystemApps() check(systemAppsUpdateTask.isSuccess()) { "failed to fetch system apps update!" } val enqueueInstall = checkForUpdates() check(enqueueInstall == ResultStatus.OK) { Loading Loading @@ -156,7 +149,7 @@ class UpdatesWorker @AssistedInject constructor( loadSettings() val appsNeededToUpdate = mutableListOf<Application>() val user = getUser() val authData = authenticatorRepository.getValidatedAuthData().data val authData = playStoreAuthManager.getValidatedAuthData().data val resultStatus: ResultStatus if (user in listOf(User.ANONYMOUS, User.GOOGLE) && authData != null) { Loading @@ -165,14 +158,20 @@ class UpdatesWorker @AssistedInject constructor( * apps from Google Play store. * The user check will be more useful in No-Google mode. */ val (apps, status) = updatesManagerRepository.getUpdates() val (apps, status) = updatesCoordinator.getAvailableUpdates( user = user, hasAuthenticatedPlayStoreSession = true ) appsNeededToUpdate.addAll(apps) resultStatus = status } else if (user == User.NO_GOOGLE) { /* * If authData is null, update apps from cleanapk only. */ val (apps, status) = updatesManagerRepository.getUpdatesOSS() val (apps, status) = updatesCoordinator.getAvailableUpdates( user = user, hasAuthenticatedPlayStoreSession = false ) appsNeededToUpdate.addAll(apps) resultStatus = status } else { Loading Loading @@ -224,9 +223,11 @@ class UpdatesWorker @AssistedInject constructor( // returns list of Pair(app, status(success|failed)) @VisibleForTesting suspend fun startUpdateProcess(appsNeededToUpdate: List<Application>): List<Pair<Application, Boolean>> { suspend fun startUpdateProcess( appsNeededToUpdate: List<Application> ): List<Pair<Application, Boolean>> { val response = mutableListOf<Pair<Application, Boolean>>() val authData = authenticatorRepository.getValidatedAuthData() val authData = playStoreAuthManager.getValidatedAuthData() val isNotLoggedIntoPersonalAccount = !authData.isValidData() || authData.data?.isAnonymous == true for (fusedApp in appsNeededToUpdate) { Loading
app/src/main/java/foundation/e/apps/data/install/workmanager/InstallWorkManager.kt +0 −4 Original line number Diff line number Diff line Loading @@ -37,10 +37,6 @@ object InstallWorkManager { return operation } fun cancelWork(context: Context, tag: String) { WorkManager.getInstance(context).cancelAllWorkByTag(tag) } fun checkWorkIsAlreadyAvailable(context: Context, tag: String): Boolean { val works = WorkManager.getInstance(context).getWorkInfosByTag(tag) try { Loading
app/src/main/java/foundation/e/apps/data/login/cleanapk/CleanApkAuthenticator.kt +2 −6 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import foundation.e.apps.data.login.core.StoreAuthResult import foundation.e.apps.data.login.core.StoreAuthenticator import foundation.e.apps.data.login.core.StoreType import foundation.e.apps.domain.model.LoginState import foundation.e.apps.domain.model.User import foundation.e.apps.domain.preferences.AppPreferencesRepository import foundation.e.apps.domain.preferences.SessionRepository import javax.inject.Inject Loading @@ -40,11 +39,8 @@ class CleanApkAuthenticator @Inject constructor( ) : StoreAuthenticator { override val storeType: StoreType = StoreType.CLEAN_APK private val user: User get() = sessionRepository.getUser() override fun isStoreActive(): Boolean { if (sessionRepository.getLoginState() == LoginState.UNAVAILABLE) { if (sessionRepository.loginState.value == LoginState.UNAVAILABLE) { /* * UNAVAILABLE login state means first login is not completed. */ Loading @@ -57,7 +53,7 @@ class CleanApkAuthenticator @Inject constructor( return StoreAuthResult( authObject = AuthObject.CleanApk( ResultSupreme.Success(Unit), user, sessionRepository.awaitUser(), ) ) } Loading