Loading app/src/main/java/foundation/e/apps/data/di/bindings/RepositoryModule.kt +8 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ import foundation.e.apps.data.exodus.repositories.PrivacyScoreRepositoryImpl import foundation.e.apps.data.fdroid.FDroidRepository import foundation.e.apps.data.fdroid.IFdroidRepository import foundation.e.apps.data.install.AppManagerImpl import foundation.e.apps.data.updates.PendingUpdatesRepositoryImpl import foundation.e.apps.domain.updates.PendingUpdatesRepository import javax.inject.Singleton @Module Loading @@ -50,4 +52,10 @@ interface RepositoryModule { @Singleton @Binds fun getPrivacyScoreRepository(privacyScoreRepositoryImpl: PrivacyScoreRepositoryImpl): PrivacyScoreRepository @Singleton @Binds fun bindPendingUpdatesRepository( pendingUpdatesRepositoryImpl: PendingUpdatesRepositoryImpl ): PendingUpdatesRepository } app/src/main/java/foundation/e/apps/data/updates/PendingUpdatesRepositoryImpl.kt 0 → 100644 +30 −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.updates import foundation.e.apps.data.application.UpdatesDao import foundation.e.apps.domain.updates.PendingUpdatesRepository import javax.inject.Inject import javax.inject.Singleton @Singleton class PendingUpdatesRepositoryImpl @Inject constructor() : PendingUpdatesRepository { override fun clearPendingUpdates() { UpdatesDao.addItemsForUpdate(emptyList()) } } app/src/main/java/foundation/e/apps/ui/settings/UpdateSourceSelectionUseCase.kt→app/src/main/java/foundation/e/apps/feature/auth/source/UpdateSourceSelectionUseCase.kt +4 −3 Original line number Diff line number Diff line Loading @@ -16,24 +16,25 @@ * */ package foundation.e.apps.ui.settings package foundation.e.apps.feature.auth.source import androidx.work.ExistingPeriodicWorkPolicy import foundation.e.apps.data.application.UpdatesDao import foundation.e.apps.domain.source.SourceSelection import foundation.e.apps.domain.source.SourceSelectionRepository import foundation.e.apps.domain.updates.PendingUpdatesRepository import foundation.e.apps.feature.auth.session.SessionStateController import foundation.e.apps.updates.PeriodicUpdatesScheduler import javax.inject.Inject class UpdateSourceSelectionUseCase @Inject constructor( private val pendingUpdatesRepository: PendingUpdatesRepository, private val periodicUpdatesScheduler: PeriodicUpdatesScheduler, private val sessionStateController: SessionStateController, private val sourceSelectionRepository: SourceSelectionRepository, ) { suspend operator fun invoke(sourceSelection: SourceSelection) { sourceSelectionRepository.saveSourceSelection(sourceSelection) UpdatesDao.addItemsForUpdate(emptyList()) pendingUpdatesRepository.clearPendingUpdates() periodicUpdatesScheduler.syncPeriodicUpdates( existingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, ) Loading app/src/main/java/foundation/e/apps/ui/settings/SettingsViewModel.kt +1 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import foundation.e.apps.domain.source.AppSource import foundation.e.apps.domain.source.SourceSelection import foundation.e.apps.domain.source.SourceSelectionRepository import foundation.e.apps.feature.auth.session.SessionRefreshException import foundation.e.apps.feature.auth.source.UpdateSourceSelectionUseCase import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted Loading app/src/test/java/foundation/e/apps/ui/settings/UpdateSourceSelectionUseCaseTest.kt→app/src/test/java/foundation/e/apps/feature/auth/source/UpdateSourceSelectionUseCaseTest.kt +13 −15 Original line number Diff line number Diff line Loading @@ -16,17 +16,17 @@ * */ package foundation.e.apps.ui.settings package foundation.e.apps.feature.auth.source import androidx.work.ExistingPeriodicWorkPolicy import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.application.UpdatesDao import foundation.e.apps.domain.auth.AuthRefreshSnapshot import foundation.e.apps.domain.auth.AuthRefreshState import foundation.e.apps.domain.auth.AuthSession import foundation.e.apps.domain.auth.AuthStore import foundation.e.apps.domain.source.SourceSelection import foundation.e.apps.domain.source.SourceSelectionRepository import foundation.e.apps.domain.updates.PendingUpdatesRepository import foundation.e.apps.feature.auth.session.SessionStateController import foundation.e.apps.updates.PeriodicUpdatesScheduler import io.mockk.coVerify Loading @@ -34,28 +34,18 @@ import io.mockk.mockk import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Test class UpdateSourceSelectionUseCaseTest { @Before fun setUp() { UpdatesDao.addItemsForUpdate(listOf(mockk(relaxed = true))) } @After fun tearDown() { UpdatesDao.addItemsForUpdate(emptyList()) } @Test fun `invoke persists selection reschedules updates clears pending updates and refreshes sessions`() = runTest { val pendingUpdatesRepository = FakePendingUpdatesRepository() val sourceSelectionRepository = FakeSourceSelectionRepository(SourceSelection.DEFAULT) val periodicUpdatesScheduler = mockk<PeriodicUpdatesScheduler>(relaxed = true) val sessionStateController = FakeSessionStateController() val useCase = UpdateSourceSelectionUseCase( pendingUpdatesRepository = pendingUpdatesRepository, periodicUpdatesScheduler = periodicUpdatesScheduler, sessionStateController = sessionStateController, sourceSelectionRepository = sourceSelectionRepository, Loading @@ -65,7 +55,7 @@ class UpdateSourceSelectionUseCaseTest { useCase(updatedSelection) assertThat(sourceSelectionRepository.currentSourceSelection()).isEqualTo(updatedSelection) assertThat(UpdatesDao.hasAnyAppsForUpdate()).isFalse() assertThat(pendingUpdatesRepository.clearPendingUpdatesCallCount).isEqualTo(1) assertThat(sessionStateController.clearLoadedSessionsCallCount).isEqualTo(1) assertThat(sessionStateController.refreshCallCount).isEqualTo(1) coVerify(exactly = 1) { Loading @@ -75,6 +65,14 @@ class UpdateSourceSelectionUseCaseTest { } } private class FakePendingUpdatesRepository : PendingUpdatesRepository { var clearPendingUpdatesCallCount = 0 override fun clearPendingUpdates() { clearPendingUpdatesCallCount += 1 } } private class FakeSourceSelectionRepository( initialSourceSelection: SourceSelection, ) : SourceSelectionRepository { Loading Loading
app/src/main/java/foundation/e/apps/data/di/bindings/RepositoryModule.kt +8 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ import foundation.e.apps.data.exodus.repositories.PrivacyScoreRepositoryImpl import foundation.e.apps.data.fdroid.FDroidRepository import foundation.e.apps.data.fdroid.IFdroidRepository import foundation.e.apps.data.install.AppManagerImpl import foundation.e.apps.data.updates.PendingUpdatesRepositoryImpl import foundation.e.apps.domain.updates.PendingUpdatesRepository import javax.inject.Singleton @Module Loading @@ -50,4 +52,10 @@ interface RepositoryModule { @Singleton @Binds fun getPrivacyScoreRepository(privacyScoreRepositoryImpl: PrivacyScoreRepositoryImpl): PrivacyScoreRepository @Singleton @Binds fun bindPendingUpdatesRepository( pendingUpdatesRepositoryImpl: PendingUpdatesRepositoryImpl ): PendingUpdatesRepository }
app/src/main/java/foundation/e/apps/data/updates/PendingUpdatesRepositoryImpl.kt 0 → 100644 +30 −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.updates import foundation.e.apps.data.application.UpdatesDao import foundation.e.apps.domain.updates.PendingUpdatesRepository import javax.inject.Inject import javax.inject.Singleton @Singleton class PendingUpdatesRepositoryImpl @Inject constructor() : PendingUpdatesRepository { override fun clearPendingUpdates() { UpdatesDao.addItemsForUpdate(emptyList()) } }
app/src/main/java/foundation/e/apps/ui/settings/UpdateSourceSelectionUseCase.kt→app/src/main/java/foundation/e/apps/feature/auth/source/UpdateSourceSelectionUseCase.kt +4 −3 Original line number Diff line number Diff line Loading @@ -16,24 +16,25 @@ * */ package foundation.e.apps.ui.settings package foundation.e.apps.feature.auth.source import androidx.work.ExistingPeriodicWorkPolicy import foundation.e.apps.data.application.UpdatesDao import foundation.e.apps.domain.source.SourceSelection import foundation.e.apps.domain.source.SourceSelectionRepository import foundation.e.apps.domain.updates.PendingUpdatesRepository import foundation.e.apps.feature.auth.session.SessionStateController import foundation.e.apps.updates.PeriodicUpdatesScheduler import javax.inject.Inject class UpdateSourceSelectionUseCase @Inject constructor( private val pendingUpdatesRepository: PendingUpdatesRepository, private val periodicUpdatesScheduler: PeriodicUpdatesScheduler, private val sessionStateController: SessionStateController, private val sourceSelectionRepository: SourceSelectionRepository, ) { suspend operator fun invoke(sourceSelection: SourceSelection) { sourceSelectionRepository.saveSourceSelection(sourceSelection) UpdatesDao.addItemsForUpdate(emptyList()) pendingUpdatesRepository.clearPendingUpdates() periodicUpdatesScheduler.syncPeriodicUpdates( existingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, ) Loading
app/src/main/java/foundation/e/apps/ui/settings/SettingsViewModel.kt +1 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import foundation.e.apps.domain.source.AppSource import foundation.e.apps.domain.source.SourceSelection import foundation.e.apps.domain.source.SourceSelectionRepository import foundation.e.apps.feature.auth.session.SessionRefreshException import foundation.e.apps.feature.auth.source.UpdateSourceSelectionUseCase import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted Loading
app/src/test/java/foundation/e/apps/ui/settings/UpdateSourceSelectionUseCaseTest.kt→app/src/test/java/foundation/e/apps/feature/auth/source/UpdateSourceSelectionUseCaseTest.kt +13 −15 Original line number Diff line number Diff line Loading @@ -16,17 +16,17 @@ * */ package foundation.e.apps.ui.settings package foundation.e.apps.feature.auth.source import androidx.work.ExistingPeriodicWorkPolicy import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.application.UpdatesDao import foundation.e.apps.domain.auth.AuthRefreshSnapshot import foundation.e.apps.domain.auth.AuthRefreshState import foundation.e.apps.domain.auth.AuthSession import foundation.e.apps.domain.auth.AuthStore import foundation.e.apps.domain.source.SourceSelection import foundation.e.apps.domain.source.SourceSelectionRepository import foundation.e.apps.domain.updates.PendingUpdatesRepository import foundation.e.apps.feature.auth.session.SessionStateController import foundation.e.apps.updates.PeriodicUpdatesScheduler import io.mockk.coVerify Loading @@ -34,28 +34,18 @@ import io.mockk.mockk import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Test class UpdateSourceSelectionUseCaseTest { @Before fun setUp() { UpdatesDao.addItemsForUpdate(listOf(mockk(relaxed = true))) } @After fun tearDown() { UpdatesDao.addItemsForUpdate(emptyList()) } @Test fun `invoke persists selection reschedules updates clears pending updates and refreshes sessions`() = runTest { val pendingUpdatesRepository = FakePendingUpdatesRepository() val sourceSelectionRepository = FakeSourceSelectionRepository(SourceSelection.DEFAULT) val periodicUpdatesScheduler = mockk<PeriodicUpdatesScheduler>(relaxed = true) val sessionStateController = FakeSessionStateController() val useCase = UpdateSourceSelectionUseCase( pendingUpdatesRepository = pendingUpdatesRepository, periodicUpdatesScheduler = periodicUpdatesScheduler, sessionStateController = sessionStateController, sourceSelectionRepository = sourceSelectionRepository, Loading @@ -65,7 +55,7 @@ class UpdateSourceSelectionUseCaseTest { useCase(updatedSelection) assertThat(sourceSelectionRepository.currentSourceSelection()).isEqualTo(updatedSelection) assertThat(UpdatesDao.hasAnyAppsForUpdate()).isFalse() assertThat(pendingUpdatesRepository.clearPendingUpdatesCallCount).isEqualTo(1) assertThat(sessionStateController.clearLoadedSessionsCallCount).isEqualTo(1) assertThat(sessionStateController.refreshCallCount).isEqualTo(1) coVerify(exactly = 1) { Loading @@ -75,6 +65,14 @@ class UpdateSourceSelectionUseCaseTest { } } private class FakePendingUpdatesRepository : PendingUpdatesRepository { var clearPendingUpdatesCallCount = 0 override fun clearPendingUpdates() { clearPendingUpdatesCallCount += 1 } } private class FakeSourceSelectionRepository( initialSourceSelection: SourceSelection, ) : SourceSelectionRepository { Loading