diff --git a/app/src/main/java/foundation/e/apps/data/application/ApplicationDataManager.kt b/app/src/main/java/foundation/e/apps/data/application/ApplicationDataManager.kt index 96290fd68c36f7b33bee3a1b85e40c13a6d4e224..2b56f9e37a190ee35d61dee42c4afd6bc79e1c4a 100644 --- a/app/src/main/java/foundation/e/apps/data/application/ApplicationDataManager.kt +++ b/app/src/main/java/foundation/e/apps/data/application/ApplicationDataManager.kt @@ -24,21 +24,23 @@ import foundation.e.apps.data.application.data.Home import foundation.e.apps.data.enums.FilterLevel import foundation.e.apps.data.enums.Source import foundation.e.apps.data.enums.Status -import foundation.e.apps.install.pkg.PwaManager +import foundation.e.apps.data.playstore.PlayStoreRepository import foundation.e.apps.install.pkg.AppLoungePackageManager +import foundation.e.apps.install.pkg.PwaManager import javax.inject.Inject import javax.inject.Singleton @Singleton class ApplicationDataManager @Inject constructor( private val appLoungePackageManager: AppLoungePackageManager, - private val pwaManager: PwaManager + private val pwaManager: PwaManager, + private val playStoreRepository: PlayStoreRepository ) { - fun updateFilterLevel(application: Application) { + suspend fun updateFilterLevel(application: Application) { application.filterLevel = getAppFilterLevel(application) } - fun prepareApps( + suspend fun prepareApps( appList: List, list: MutableList, value: String @@ -53,14 +55,16 @@ class ApplicationDataManager @Inject constructor( } } - fun getAppFilterLevel(application: Application): FilterLevel { + suspend fun getAppFilterLevel(application: Application): FilterLevel { return when { application.package_name.isBlank() -> FilterLevel.UNKNOWN !application.isFree && application.price.isBlank() -> FilterLevel.UI application.source == Source.PWA || application.source == Source.OPEN_SOURCE -> FilterLevel.NONE application.source == Source.SYSTEM_APP -> FilterLevel.NONE !isRestricted(application) -> FilterLevel.NONE + !isApplicationVisible(application) -> FilterLevel.DATA application.originalSize == 0L -> FilterLevel.UI + !isDownloadable(application) -> FilterLevel.UI else -> FilterLevel.NONE } } @@ -69,6 +73,28 @@ class ApplicationDataManager @Inject constructor( return application.restriction != Constants.Restriction.NOT_RESTRICTED } + /* + * Some apps are simply not visible. + * Example: com.skype.m2 + */ + private suspend fun isApplicationVisible(application: Application): Boolean { + return kotlin.runCatching { playStoreRepository.getAppDetails(application.package_name) }.isSuccess + } + + /* + * Some apps are visible but not downloadable. + * Example: com.riotgames.league.wildrift + */ + private suspend fun isDownloadable(application: Application): Boolean { + return kotlin.runCatching { + playStoreRepository.getDownloadInfo( + application.package_name, + application.latest_version_code, + application.offer_type, + ) + }.isSuccess + } + fun updateStatus(application: Application) { if (application.status != Status.INSTALLATION_ISSUE) { application.status = getFusedAppInstallationStatus(application) diff --git a/app/src/main/java/foundation/e/apps/data/application/apps/AppsApiImpl.kt b/app/src/main/java/foundation/e/apps/data/application/apps/AppsApiImpl.kt index 9fb19e1ab5c0358db0da83240203135672be9aac..d40c13f5fa44bb3e8d56f82f12f3e5a0ae56351e 100644 --- a/app/src/main/java/foundation/e/apps/data/application/apps/AppsApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/application/apps/AppsApiImpl.kt @@ -23,12 +23,12 @@ import foundation.e.apps.data.application.ApplicationDataManager import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.enums.FilterLevel import foundation.e.apps.data.enums.ResultStatus +import foundation.e.apps.data.enums.Source import foundation.e.apps.data.enums.Status import foundation.e.apps.data.enums.isUnFiltered import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.ui.applicationlist.ApplicationDiffUtil import javax.inject.Inject -import foundation.e.apps.data.enums.Source class AppsApiImpl @Inject constructor( private val stores: Stores, @@ -50,7 +50,7 @@ class AppsApiImpl @Inject constructor( /* * Handy method to run on an instance of FusedApp to update its filter level. */ - private fun Application.updateFilterLevel() { + private suspend fun Application.updateFilterLevel() { this.filterLevel = applicationDataManager.getAppFilterLevel(this) } @@ -116,7 +116,7 @@ class AppsApiImpl @Inject constructor( * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5174 */ - private fun handleFilteredApps( + private suspend fun handleFilteredApps( app: Application, applicationList: MutableList ) { diff --git a/app/src/main/java/foundation/e/apps/data/cleanapk/repositories/HomeConverter.kt b/app/src/main/java/foundation/e/apps/data/cleanapk/repositories/HomeConverter.kt index b6e45dce2b75a94db9f05ecb42a7689c5d86f32d..77ef5955990e6a0e713fb65c9ec7b9671ccd4866 100644 --- a/app/src/main/java/foundation/e/apps/data/cleanapk/repositories/HomeConverter.kt +++ b/app/src/main/java/foundation/e/apps/data/cleanapk/repositories/HomeConverter.kt @@ -13,7 +13,7 @@ class HomeConverter @Inject constructor( private val applicationDataManager: ApplicationDataManager ) { - fun toGenericHome(cleanApkHome: CleanApkHome, appType: String): List { + suspend fun toGenericHome(cleanApkHome: CleanApkHome, appType: String): List { val list = mutableListOf() openSourceCategories.forEach { (key, value) -> @@ -55,4 +55,4 @@ class HomeConverter @Inject constructor( "discover" to context.getString(R.string.discover) ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt b/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt index 2a55f4c82768e2ec82a1b8356989de8d6852d65a..bbca2485ad990bedfff452a80b6618633ee579c7 100644 --- a/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt @@ -19,9 +19,7 @@ package foundation.e.apps.data.playstore import android.content.Context -import android.os.Build import com.aurora.gplayapi.SearchSuggestEntry -import com.aurora.gplayapi.data.models.App as GplayApp import com.aurora.gplayapi.data.models.Category import com.aurora.gplayapi.data.models.ContentRating import com.aurora.gplayapi.data.models.File @@ -35,6 +33,7 @@ import com.aurora.gplayapi.helpers.web.WebCategoryHelper import com.aurora.gplayapi.helpers.web.WebCategoryStreamHelper import com.aurora.gplayapi.helpers.web.WebSearchHelper import com.aurora.gplayapi.helpers.web.WebTopChartsHelper +import dagger.Lazy import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R import foundation.e.apps.data.StoreRepository @@ -51,12 +50,13 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import timber.log.Timber import javax.inject.Inject +import com.aurora.gplayapi.data.models.App as GplayApp class PlayStoreRepository @Inject constructor( @ApplicationContext private val context: Context, private val gPlayHttpClient: GPlayHttpClient, private val authenticatorRepository: AuthenticatorRepository, - private val applicationDataManager: ApplicationDataManager + private val applicationDataManager: Lazy // Used Lazy to break circular dependency ) : StoreRepository { override suspend fun getHomeScreenData(list: MutableList): List { val homeScreenData = mutableMapOf>() @@ -74,8 +74,8 @@ class PlayStoreRepository @Inject constructor( homeScreenData.map { val fusedApps = it.value.map { app -> app.apply { - applicationDataManager.updateStatus(this) - applicationDataManager.updateFilterLevel(this) + applicationDataManager.get().updateStatus(this) + applicationDataManager.get().updateFilterLevel(this) source = Source.PLAY_STORE } } diff --git a/app/src/test/java/foundation/e/apps/apps/AppsApiTest.kt b/app/src/test/java/foundation/e/apps/apps/AppsApiTest.kt index 812e3c54126f60d7f8a0edeca5b71c7cc59b68db..395ec1dd8879114bffc375c1a7eac918203116b5 100644 --- a/app/src/test/java/foundation/e/apps/apps/AppsApiTest.kt +++ b/app/src/test/java/foundation/e/apps/apps/AppsApiTest.kt @@ -24,16 +24,16 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.aurora.gplayapi.Constants import foundation.e.apps.FakeAppLoungePreference import foundation.e.apps.data.Stores -import foundation.e.apps.data.enums.FilterLevel -import foundation.e.apps.data.enums.Status import foundation.e.apps.data.application.ApplicationDataManager import foundation.e.apps.data.application.apps.AppsApi import foundation.e.apps.data.application.apps.AppsApiImpl import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.enums.FilterLevel import foundation.e.apps.data.enums.Source +import foundation.e.apps.data.enums.Status import foundation.e.apps.data.playstore.PlayStoreRepository -import foundation.e.apps.install.pkg.PwaManager import foundation.e.apps.install.pkg.AppLoungePackageManager +import foundation.e.apps.install.pkg.PwaManager import foundation.e.apps.util.MainCoroutineRule import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -73,7 +73,7 @@ class AppsApiTest { private lateinit var context: Context @Mock - private lateinit var gPlayAPIRepository: PlayStoreRepository + private lateinit var playStoreRepository: PlayStoreRepository @Mock private lateinit var stores: Stores @@ -92,7 +92,7 @@ class AppsApiTest { formatterMocked = Mockito.mockStatic(Formatter::class.java) preferenceManagerModule = FakeAppLoungePreference(context) applicationDataManager = - ApplicationDataManager(appLoungePackageManager, pwaManager) + ApplicationDataManager(appLoungePackageManager, pwaManager, playStoreRepository) appsApi = AppsApiImpl( stores, applicationDataManager @@ -467,11 +467,11 @@ class AppsApiTest { this.restriction = Constants.Restriction.UNKNOWN } - Mockito.`when`(gPlayAPIRepository.getAppDetails(fusedApp.package_name)) + Mockito.`when`(playStoreRepository.getAppDetails(fusedApp.package_name)) .thenReturn(Application(fusedApp.package_name)) Mockito.`when`( - gPlayAPIRepository.getDownloadInfo( + playStoreRepository.getDownloadInfo( fusedApp.package_name, fusedApp.latest_version_code, fusedApp.offer_type, diff --git a/app/src/test/java/foundation/e/apps/category/CategoryApiTest.kt b/app/src/test/java/foundation/e/apps/category/CategoryApiTest.kt index e0718f55f31c015af7b0be09e7d97631545b086e..7c522dee32965cd15e4175a5e51661f937b41659 100644 --- a/app/src/test/java/foundation/e/apps/category/CategoryApiTest.kt +++ b/app/src/test/java/foundation/e/apps/category/CategoryApiTest.kt @@ -80,7 +80,7 @@ class CategoryApiTest { private lateinit var cleanApkPWARepository: CleanApkPwaRepository @Mock - private lateinit var gPlayAPIRepository: PlayStoreRepository + private lateinit var playStoreRepository: PlayStoreRepository @Mock private lateinit var appLoungePreference: AppLoungePreference @@ -93,12 +93,12 @@ class CategoryApiTest { fun setup() { MockitoAnnotations.openMocks(this) val applicationDataManager = - ApplicationDataManager(appLoungePackageManager, pwaManager) + ApplicationDataManager(appLoungePackageManager, pwaManager, playStoreRepository) - fakeStores = Stores(gPlayAPIRepository, cleanApkAppsRepository, cleanApkPWARepository, appLoungePreference) + fakeStores = Stores(playStoreRepository, cleanApkAppsRepository, cleanApkPWARepository, appLoungePreference) val appSourcesContainer = - AppSourcesContainer(gPlayAPIRepository, cleanApkAppsRepository, cleanApkPWARepository) + AppSourcesContainer(playStoreRepository, cleanApkAppsRepository, cleanApkPWARepository) categoryApi = CategoryApiImpl( context, appSourcesContainer, @@ -158,7 +158,7 @@ class CategoryApiTest { val categories = listOf(Category(), Category(), Category(), Category()) Mockito.`when`( - gPlayAPIRepository.getCategories(CategoryType.APPLICATION) + playStoreRepository.getCategories(CategoryType.APPLICATION) ).thenReturn(categories) Mockito.`when`(appLoungePreference.isPlayStoreSelected()).thenReturn(true) @@ -175,7 +175,7 @@ class CategoryApiTest { @Test fun `getCategory when gplay source is selected return error`() = runTest { Mockito.`when`( - gPlayAPIRepository.getCategories(CategoryType.APPLICATION) + playStoreRepository.getCategories(CategoryType.APPLICATION) ).thenThrow(RuntimeException()) Mockito.`when`(appLoungePreference.isPlayStoreSelected()).thenReturn(true) @@ -210,7 +210,7 @@ class CategoryApiTest { ).thenReturn(pwaResponse) Mockito.`when`( - gPlayAPIRepository.getCategories(CategoryType.APPLICATION) + playStoreRepository.getCategories(CategoryType.APPLICATION) ).thenReturn(gplayCategories) Mockito.`when`(context.getString(eq(R.string.open_source))).thenReturn("Open source") diff --git a/app/src/test/java/foundation/e/apps/fused/SearchApiImplTest.kt b/app/src/test/java/foundation/e/apps/fused/SearchApiImplTest.kt index 3bf8dac88886a480113cc2b35067010503f26f3e..24107984a45d153435c311384ee44a7c3a6a2e78 100644 --- a/app/src/test/java/foundation/e/apps/fused/SearchApiImplTest.kt +++ b/app/src/test/java/foundation/e/apps/fused/SearchApiImplTest.kt @@ -25,19 +25,19 @@ import com.aurora.gplayapi.data.models.SearchBundle import foundation.e.apps.FakeAppLoungePreference import foundation.e.apps.data.AppSourcesContainer import foundation.e.apps.data.Stores -import foundation.e.apps.data.cleanapk.data.search.Search -import foundation.e.apps.data.enums.Status -import foundation.e.apps.data.application.search.SearchApiImpl import foundation.e.apps.data.application.ApplicationDataManager import foundation.e.apps.data.application.apps.AppsApi import foundation.e.apps.data.application.apps.AppsApiImpl import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.application.search.SearchApiImpl +import foundation.e.apps.data.cleanapk.data.search.Search import foundation.e.apps.data.cleanapk.repositories.CleanApkAppsRepository import foundation.e.apps.data.cleanapk.repositories.CleanApkPwaRepository import foundation.e.apps.data.enums.Source +import foundation.e.apps.data.enums.Status import foundation.e.apps.data.playstore.PlayStoreRepository -import foundation.e.apps.install.pkg.PwaManager import foundation.e.apps.install.pkg.AppLoungePackageManager +import foundation.e.apps.install.pkg.PwaManager import foundation.e.apps.util.MainCoroutineRule import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -86,7 +86,7 @@ class SearchApiImplTest { private lateinit var cleanApkPWARepository: CleanApkPwaRepository @Mock - private lateinit var gPlayAPIRepository: PlayStoreRepository + private lateinit var playStoreRepository: PlayStoreRepository @Mock private lateinit var stores: Stores @@ -105,9 +105,9 @@ class SearchApiImplTest { formatterMocked = Mockito.mockStatic(Formatter::class.java) preferenceManagerModule = FakeAppLoungePreference(context) applicationDataManager = - ApplicationDataManager(appLoungePackageManager, pwaManager) + ApplicationDataManager(appLoungePackageManager, pwaManager, playStoreRepository) val appSourcesContainer = - AppSourcesContainer(gPlayAPIRepository, cleanApkAppsRepository, cleanApkPWARepository) + AppSourcesContainer(playStoreRepository, cleanApkAppsRepository, cleanApkPWARepository) appsApi = AppsApiImpl( stores, applicationDataManager, @@ -204,10 +204,10 @@ class SearchApiImplTest { formatterMocked.`when` { Formatter.formatFileSize(any(), any()) }.thenReturn("15MB") if (willThrowException) { - Mockito.`when`(gPlayAPIRepository.getAppDetails("com.search.package")) + Mockito.`when`(playStoreRepository.getAppDetails("com.search.package")) .thenThrow(RuntimeException()) } else { - Mockito.`when`(gPlayAPIRepository.getAppDetails(eq("com.search.package"))) + Mockito.`when`(playStoreRepository.getAppDetails(eq("com.search.package"))) .thenReturn(gplayPackageResult) } @@ -224,7 +224,7 @@ class SearchApiImplTest { Mockito.`when`(cleanApkAppsRepository.getAppDetails(any())) .thenReturn(Application()) - Mockito.`when`(gPlayAPIRepository.getSearchResults(eq("com.search.package"))) + Mockito.`when`(playStoreRepository.getSearchResults(eq("com.search.package"))) .thenReturn(apps) }