From c2613a6135abc8f1ce3a0baf991ef7660ac27627 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Tue, 22 Apr 2025 18:11:49 +0600 Subject: [PATCH 1/3] refactor: hide apps from search and other places when they are not visible and downloadable --- .../application/ApplicationDataManager.kt | 36 ++++++++++++++++--- .../apps/data/application/apps/AppsApiImpl.kt | 6 ++-- .../cleanapk/repositories/HomeConverter.kt | 4 +-- .../data/playstore/PlayStoreRepository.kt | 10 +++--- 4 files changed, 41 insertions(+), 15 deletions(-) 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 96290fd68..2b56f9e37 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 9fb19e1ab..d40c13f5f 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 b6e45dce2..77ef59559 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 2a55f4c82..bbca2485a 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 } } -- GitLab From 68d16f25a8f1735792b28f77e21306e2d31544d9 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Tue, 22 Apr 2025 18:18:18 +0600 Subject: [PATCH 2/3] refactor: fix failing tests --- .../test/java/foundation/e/apps/apps/AppsApiTest.kt | 8 ++++---- .../java/foundation/e/apps/category/CategoryApiTest.kt | 2 +- .../java/foundation/e/apps/fused/SearchApiImplTest.kt | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) 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 812e3c541..eae931376 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 @@ -92,7 +92,7 @@ class AppsApiTest { formatterMocked = Mockito.mockStatic(Formatter::class.java) preferenceManagerModule = FakeAppLoungePreference(context) applicationDataManager = - ApplicationDataManager(appLoungePackageManager, pwaManager) + ApplicationDataManager(appLoungePackageManager, pwaManager, gPlayAPIRepository) appsApi = AppsApiImpl( stores, applicationDataManager 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 e0718f55f..9d5cc343d 100644 --- a/app/src/test/java/foundation/e/apps/category/CategoryApiTest.kt +++ b/app/src/test/java/foundation/e/apps/category/CategoryApiTest.kt @@ -93,7 +93,7 @@ class CategoryApiTest { fun setup() { MockitoAnnotations.openMocks(this) val applicationDataManager = - ApplicationDataManager(appLoungePackageManager, pwaManager) + ApplicationDataManager(appLoungePackageManager, pwaManager, gPlayAPIRepository) fakeStores = Stores(gPlayAPIRepository, cleanApkAppsRepository, cleanApkPWARepository, appLoungePreference) 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 3bf8dac88..3f3812f15 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 @@ -105,7 +105,7 @@ class SearchApiImplTest { formatterMocked = Mockito.mockStatic(Formatter::class.java) preferenceManagerModule = FakeAppLoungePreference(context) applicationDataManager = - ApplicationDataManager(appLoungePackageManager, pwaManager) + ApplicationDataManager(appLoungePackageManager, pwaManager, gPlayAPIRepository) val appSourcesContainer = AppSourcesContainer(gPlayAPIRepository, cleanApkAppsRepository, cleanApkPWARepository) appsApi = AppsApiImpl( -- GitLab From cc0cba5d285626b735f84f148dffcb584bffb009 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Thu, 24 Apr 2025 10:06:10 +0600 Subject: [PATCH 3/3] refactor: rename PlayStoreRepository instances in tests --- .../java/foundation/e/apps/apps/AppsApiTest.kt | 8 ++++---- .../foundation/e/apps/category/CategoryApiTest.kt | 14 +++++++------- .../foundation/e/apps/fused/SearchApiImplTest.kt | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) 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 eae931376..395ec1dd8 100644 --- a/app/src/test/java/foundation/e/apps/apps/AppsApiTest.kt +++ b/app/src/test/java/foundation/e/apps/apps/AppsApiTest.kt @@ -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, gPlayAPIRepository) + 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 9d5cc343d..7c522dee3 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, gPlayAPIRepository) + 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 3f3812f15..24107984a 100644 --- a/app/src/test/java/foundation/e/apps/fused/SearchApiImplTest.kt +++ b/app/src/test/java/foundation/e/apps/fused/SearchApiImplTest.kt @@ -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, gPlayAPIRepository) + 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) } -- GitLab