Loading .gitignore +1 −0 Original line number Diff line number Diff line Loading @@ -8,3 +8,4 @@ .externalNativeBuild .cxx local.properties **/build/ app/build.gradle +12 −0 Original line number Diff line number Diff line Loading @@ -96,6 +96,15 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } testOptions { unitTests { includeAndroidResources = true all { systemProperty 'robolectric.usePreinstrumentedJars', 'false' } } } signingConfigs { platformConfig { storeFile file("keystore/platform.jks") Loading Loading @@ -195,6 +204,8 @@ dependencies { // Project dependencies implementation(project(":auth-data-lib")) implementation(project(":parental-control-data")) implementation(project(":domain")) implementation(project(":data")) // eFoundation libraries implementation(libs.telemetry) Loading Loading @@ -262,6 +273,7 @@ dependencies { testImplementation(libs.core.testing) testImplementation(libs.mockk) testImplementation(libs.robolectric) testImplementation(libs.guava) // Coil and PhotoView implementation(libs.coil) Loading app/src/main/java/foundation/e/apps/AppLoungeApplication.kt +35 −26 Original line number Diff line number Diff line Loading @@ -34,18 +34,15 @@ import foundation.e.apps.data.install.AppInstallDAO import foundation.e.apps.data.install.pkg.AppLoungePackageManager import foundation.e.apps.data.install.pkg.PkgManagerBR import foundation.e.apps.data.install.updates.UpdatesWorkManager import foundation.e.apps.data.install.workmanager.InstallWorkManager import foundation.e.apps.data.preference.AppLoungeDataStore import foundation.e.apps.data.preference.AppLoungePreference import foundation.e.apps.data.system.CustomUncaughtExceptionHandler import foundation.e.apps.domain.preferences.SessionRepository import foundation.e.apps.domain.preferences.updateinterval.GetUpdateIntervalUseCase import foundation.e.apps.domain.preferences.updateinterval.MigrateAnonymousUserUpdateIntervalUseCase import foundation.e.apps.ui.setup.tos.TOS_VERSION import foundation.e.lib.telemetry.Telemetry import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import timber.log.Timber import timber.log.Timber.Forest.plant import java.util.concurrent.Executors Loading @@ -61,12 +58,6 @@ class AppLoungeApplication : Application(), Configuration.Provider { @Inject lateinit var workerFactory: HiltWorkerFactory @Inject lateinit var appLoungeDataStore: AppLoungeDataStore @Inject lateinit var appLoungePreference: AppLoungePreference @Inject lateinit var appInstallDao: AppInstallDAO Loading @@ -77,21 +68,30 @@ class AppLoungeApplication : Application(), Configuration.Provider { @IoCoroutineScope lateinit var coroutineScope: CoroutineScope @Inject lateinit var sessionRepository: SessionRepository @Inject lateinit var getUpdateIntervalUseCase: GetUpdateIntervalUseCase @Inject lateinit var migrateAnonymousUserUpdateIntervalUseCase: MigrateAnonymousUserUpdateIntervalUseCase @Inject lateinit var pkgManagerBR: PkgManagerBR @RequiresApi(Build.VERSION_CODES.TIRAMISU) override fun onCreate() { super.onCreate() Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler) InstallWorkManager.context = this // Register broadcast receiver for package manager val pkgManagerBR = object : PkgManagerBR() {} registerReceiver(pkgManagerBR, appLoungePackageManager.getFilter(), RECEIVER_EXPORTED) val currentVersion = runBlocking { appLoungeDataStore.tosVersion.first() } coroutineScope.launch { val currentVersion = sessionRepository.awaitTosVersion() if (!currentVersion.contentEquals(TOS_VERSION)) { MainScope().launch { appLoungeDataStore.saveTOCStatus(false, "") sessionRepository.saveTocStatus(false, "") } } Loading @@ -114,15 +114,24 @@ class AppLoungeApplication : Application(), Configuration.Provider { }) } appLoungePreference.migrateAnonymousUserUpdateInterval() coroutineScope.launch { migrateAnonymousUserUpdateIntervalUseCase() val updateInterval = getUpdateIntervalUseCase() UpdatesWorkManager.enqueueWork( this, appLoungePreference.getUpdateInterval(), this@AppLoungeApplication, updateInterval, ExistingPeriodicWorkPolicy.KEEP ) } // Robolectric eagerly creates the real Application for many unrelated tests. // Skip install DB cleanup there to avoid background Room work leaking across tests. if (!isRunningUnderRobolectric()) { removeStalledInstallationFromDb() } } private fun isRunningUnderRobolectric(): Boolean = Build.FINGERPRINT == "robolectric" private fun removeStalledInstallationFromDb() = coroutineScope.launch { val existingInstallations = appInstallDao.getItemInInstallation().toMutableList() Loading app/src/main/java/foundation/e/apps/data/DownloadManager.kt +3 −3 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import androidx.core.net.toUri import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.OpenForTesting import foundation.e.apps.R import foundation.e.apps.data.preference.AppLoungePreference import foundation.e.apps.domain.preferences.AppPreferencesRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay Loading @@ -48,7 +48,7 @@ class DownloadManager @Inject constructor( private val downloadManager: DownloadManager, @Named("cacheDir") private val cacheDir: String, private val downloadManagerQuery: DownloadManager.Query, private val appLoungePreference: AppLoungePreference, private val appPreferencesRepository: AppPreferencesRepository, ) { private val downloadsMaps = HashMap<Long, Boolean>() Loading Loading @@ -82,7 +82,7 @@ class DownloadManager @Inject constructor( val request = DownloadManager.Request(url.toUri()) .setTitle(context.getString(R.string.downloading)) .setDestinationUri(Uri.fromFile(downloadFile)) if (appLoungePreference.isOnlyUnmeteredNetworkEnabled()) { if (appPreferencesRepository.isOnlyUnmeteredNetworkEnabled()) { // Set to true by default for Download requests request.setAllowedOverMetered(false) } Loading app/src/main/java/foundation/e/apps/data/Stores.kt +13 −13 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ import foundation.e.apps.data.enums.Source.OPEN_SOURCE import foundation.e.apps.data.enums.Source.PLAY_STORE import foundation.e.apps.data.enums.Source.PWA import foundation.e.apps.data.playstore.PlayStoreRepository import foundation.e.apps.data.preference.AppLoungePreference import foundation.e.apps.domain.preferences.AppPreferencesRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow Loading @@ -39,14 +39,14 @@ class Stores @Inject constructor( playStoreRepository: PlayStoreRepository, cleanApkAppsRepository: CleanApkAppsRepository, cleanApkPwaRepository: CleanApkPwaRepository, appLoungePreference: AppLoungePreference appPreferencesRepository: AppPreferencesRepository ) { private val storeConfigs: Map<Source, StoreConfig> = buildStoreConfigs( playStoreRepository, cleanApkAppsRepository, cleanApkPwaRepository, appLoungePreference appPreferencesRepository ) private val searchEligibleSources = storeConfigs.keys Loading Loading @@ -107,24 +107,24 @@ internal fun buildStoreConfigs( playStoreRepository: PlayStoreRepository, cleanApkAppsRepository: CleanApkAppsRepository, cleanApkPwaRepository: CleanApkPwaRepository, appLoungePreference: AppLoungePreference appPreferencesRepository: AppPreferencesRepository ): Map<Source, StoreConfig> = mapOf( PLAY_STORE to StoreConfig( repository = playStoreRepository, isEnabled = { appLoungePreference.isPlayStoreSelected() }, enable = { appLoungePreference.enablePlayStore() }, disable = { appLoungePreference.disablePlayStore() }, isEnabled = { appPreferencesRepository.isPlayStoreSelected() }, enable = { appPreferencesRepository.enablePlayStore() }, disable = { appPreferencesRepository.disablePlayStore() }, ), OPEN_SOURCE to StoreConfig( repository = cleanApkAppsRepository, isEnabled = { appLoungePreference.isOpenSourceSelected() }, enable = { appLoungePreference.enableOpenSource() }, disable = { appLoungePreference.disableOpenSource() }, isEnabled = { appPreferencesRepository.isOpenSourceSelected() }, enable = { appPreferencesRepository.enableOpenSource() }, disable = { appPreferencesRepository.disableOpenSource() }, ), PWA to StoreConfig( repository = cleanApkPwaRepository, isEnabled = { appLoungePreference.isPWASelected() }, enable = { appLoungePreference.enablePwa() }, disable = { appLoungePreference.disablePwa() }, isEnabled = { appPreferencesRepository.isPWASelected() }, enable = { appPreferencesRepository.enablePwa() }, disable = { appPreferencesRepository.disablePwa() }, ), ) Loading
.gitignore +1 −0 Original line number Diff line number Diff line Loading @@ -8,3 +8,4 @@ .externalNativeBuild .cxx local.properties **/build/
app/build.gradle +12 −0 Original line number Diff line number Diff line Loading @@ -96,6 +96,15 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } testOptions { unitTests { includeAndroidResources = true all { systemProperty 'robolectric.usePreinstrumentedJars', 'false' } } } signingConfigs { platformConfig { storeFile file("keystore/platform.jks") Loading Loading @@ -195,6 +204,8 @@ dependencies { // Project dependencies implementation(project(":auth-data-lib")) implementation(project(":parental-control-data")) implementation(project(":domain")) implementation(project(":data")) // eFoundation libraries implementation(libs.telemetry) Loading Loading @@ -262,6 +273,7 @@ dependencies { testImplementation(libs.core.testing) testImplementation(libs.mockk) testImplementation(libs.robolectric) testImplementation(libs.guava) // Coil and PhotoView implementation(libs.coil) Loading
app/src/main/java/foundation/e/apps/AppLoungeApplication.kt +35 −26 Original line number Diff line number Diff line Loading @@ -34,18 +34,15 @@ import foundation.e.apps.data.install.AppInstallDAO import foundation.e.apps.data.install.pkg.AppLoungePackageManager import foundation.e.apps.data.install.pkg.PkgManagerBR import foundation.e.apps.data.install.updates.UpdatesWorkManager import foundation.e.apps.data.install.workmanager.InstallWorkManager import foundation.e.apps.data.preference.AppLoungeDataStore import foundation.e.apps.data.preference.AppLoungePreference import foundation.e.apps.data.system.CustomUncaughtExceptionHandler import foundation.e.apps.domain.preferences.SessionRepository import foundation.e.apps.domain.preferences.updateinterval.GetUpdateIntervalUseCase import foundation.e.apps.domain.preferences.updateinterval.MigrateAnonymousUserUpdateIntervalUseCase import foundation.e.apps.ui.setup.tos.TOS_VERSION import foundation.e.lib.telemetry.Telemetry import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import timber.log.Timber import timber.log.Timber.Forest.plant import java.util.concurrent.Executors Loading @@ -61,12 +58,6 @@ class AppLoungeApplication : Application(), Configuration.Provider { @Inject lateinit var workerFactory: HiltWorkerFactory @Inject lateinit var appLoungeDataStore: AppLoungeDataStore @Inject lateinit var appLoungePreference: AppLoungePreference @Inject lateinit var appInstallDao: AppInstallDAO Loading @@ -77,21 +68,30 @@ class AppLoungeApplication : Application(), Configuration.Provider { @IoCoroutineScope lateinit var coroutineScope: CoroutineScope @Inject lateinit var sessionRepository: SessionRepository @Inject lateinit var getUpdateIntervalUseCase: GetUpdateIntervalUseCase @Inject lateinit var migrateAnonymousUserUpdateIntervalUseCase: MigrateAnonymousUserUpdateIntervalUseCase @Inject lateinit var pkgManagerBR: PkgManagerBR @RequiresApi(Build.VERSION_CODES.TIRAMISU) override fun onCreate() { super.onCreate() Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler) InstallWorkManager.context = this // Register broadcast receiver for package manager val pkgManagerBR = object : PkgManagerBR() {} registerReceiver(pkgManagerBR, appLoungePackageManager.getFilter(), RECEIVER_EXPORTED) val currentVersion = runBlocking { appLoungeDataStore.tosVersion.first() } coroutineScope.launch { val currentVersion = sessionRepository.awaitTosVersion() if (!currentVersion.contentEquals(TOS_VERSION)) { MainScope().launch { appLoungeDataStore.saveTOCStatus(false, "") sessionRepository.saveTocStatus(false, "") } } Loading @@ -114,15 +114,24 @@ class AppLoungeApplication : Application(), Configuration.Provider { }) } appLoungePreference.migrateAnonymousUserUpdateInterval() coroutineScope.launch { migrateAnonymousUserUpdateIntervalUseCase() val updateInterval = getUpdateIntervalUseCase() UpdatesWorkManager.enqueueWork( this, appLoungePreference.getUpdateInterval(), this@AppLoungeApplication, updateInterval, ExistingPeriodicWorkPolicy.KEEP ) } // Robolectric eagerly creates the real Application for many unrelated tests. // Skip install DB cleanup there to avoid background Room work leaking across tests. if (!isRunningUnderRobolectric()) { removeStalledInstallationFromDb() } } private fun isRunningUnderRobolectric(): Boolean = Build.FINGERPRINT == "robolectric" private fun removeStalledInstallationFromDb() = coroutineScope.launch { val existingInstallations = appInstallDao.getItemInInstallation().toMutableList() Loading
app/src/main/java/foundation/e/apps/data/DownloadManager.kt +3 −3 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import androidx.core.net.toUri import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.OpenForTesting import foundation.e.apps.R import foundation.e.apps.data.preference.AppLoungePreference import foundation.e.apps.domain.preferences.AppPreferencesRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay Loading @@ -48,7 +48,7 @@ class DownloadManager @Inject constructor( private val downloadManager: DownloadManager, @Named("cacheDir") private val cacheDir: String, private val downloadManagerQuery: DownloadManager.Query, private val appLoungePreference: AppLoungePreference, private val appPreferencesRepository: AppPreferencesRepository, ) { private val downloadsMaps = HashMap<Long, Boolean>() Loading Loading @@ -82,7 +82,7 @@ class DownloadManager @Inject constructor( val request = DownloadManager.Request(url.toUri()) .setTitle(context.getString(R.string.downloading)) .setDestinationUri(Uri.fromFile(downloadFile)) if (appLoungePreference.isOnlyUnmeteredNetworkEnabled()) { if (appPreferencesRepository.isOnlyUnmeteredNetworkEnabled()) { // Set to true by default for Download requests request.setAllowedOverMetered(false) } Loading
app/src/main/java/foundation/e/apps/data/Stores.kt +13 −13 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ import foundation.e.apps.data.enums.Source.OPEN_SOURCE import foundation.e.apps.data.enums.Source.PLAY_STORE import foundation.e.apps.data.enums.Source.PWA import foundation.e.apps.data.playstore.PlayStoreRepository import foundation.e.apps.data.preference.AppLoungePreference import foundation.e.apps.domain.preferences.AppPreferencesRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow Loading @@ -39,14 +39,14 @@ class Stores @Inject constructor( playStoreRepository: PlayStoreRepository, cleanApkAppsRepository: CleanApkAppsRepository, cleanApkPwaRepository: CleanApkPwaRepository, appLoungePreference: AppLoungePreference appPreferencesRepository: AppPreferencesRepository ) { private val storeConfigs: Map<Source, StoreConfig> = buildStoreConfigs( playStoreRepository, cleanApkAppsRepository, cleanApkPwaRepository, appLoungePreference appPreferencesRepository ) private val searchEligibleSources = storeConfigs.keys Loading Loading @@ -107,24 +107,24 @@ internal fun buildStoreConfigs( playStoreRepository: PlayStoreRepository, cleanApkAppsRepository: CleanApkAppsRepository, cleanApkPwaRepository: CleanApkPwaRepository, appLoungePreference: AppLoungePreference appPreferencesRepository: AppPreferencesRepository ): Map<Source, StoreConfig> = mapOf( PLAY_STORE to StoreConfig( repository = playStoreRepository, isEnabled = { appLoungePreference.isPlayStoreSelected() }, enable = { appLoungePreference.enablePlayStore() }, disable = { appLoungePreference.disablePlayStore() }, isEnabled = { appPreferencesRepository.isPlayStoreSelected() }, enable = { appPreferencesRepository.enablePlayStore() }, disable = { appPreferencesRepository.disablePlayStore() }, ), OPEN_SOURCE to StoreConfig( repository = cleanApkAppsRepository, isEnabled = { appLoungePreference.isOpenSourceSelected() }, enable = { appLoungePreference.enableOpenSource() }, disable = { appLoungePreference.disableOpenSource() }, isEnabled = { appPreferencesRepository.isOpenSourceSelected() }, enable = { appPreferencesRepository.enableOpenSource() }, disable = { appPreferencesRepository.disableOpenSource() }, ), PWA to StoreConfig( repository = cleanApkPwaRepository, isEnabled = { appLoungePreference.isPWASelected() }, enable = { appLoungePreference.enablePwa() }, disable = { appLoungePreference.disablePwa() }, isEnabled = { appPreferencesRepository.isPWASelected() }, enable = { appPreferencesRepository.enablePwa() }, disable = { appPreferencesRepository.disablePwa() }, ), )