From b00bfa8320393765ae2efb90dc5fd5a7647f3156 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Thu, 23 Nov 2023 21:56:11 +0600 Subject: [PATCH 1/3] HomeApi is introduced --- .../e/apps/data/application/ApplicationApi.kt | 4 - .../data/application/ApplicationApiImpl.kt | 224 +-------------- .../application/ApplicationDataManager.kt | 120 ++++++++ .../data/application/ApplicationRepository.kt | 7 +- .../e/apps/data/application/HomeApi.kt | 30 ++ .../e/apps/data/application/HomeApiImpl.kt | 258 ++++++++++++++++++ .../apps/data/application/data/Application.kt | 6 +- .../apps/data/application/utils/Extensions.kt | 69 +++++ .../java/foundation/e/apps/di/DataModule.kt | 18 ++ 9 files changed, 515 insertions(+), 221 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/data/application/ApplicationDataManager.kt create mode 100644 app/src/main/java/foundation/e/apps/data/application/HomeApi.kt create mode 100644 app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt create mode 100644 app/src/main/java/foundation/e/apps/data/application/utils/Extensions.kt create mode 100644 app/src/main/java/foundation/e/apps/di/DataModule.kt diff --git a/app/src/main/java/foundation/e/apps/data/application/ApplicationApi.kt b/app/src/main/java/foundation/e/apps/data/application/ApplicationApi.kt index 7b81cc960..468ba9190 100644 --- a/app/src/main/java/foundation/e/apps/data/application/ApplicationApi.kt +++ b/app/src/main/java/foundation/e/apps/data/application/ApplicationApi.kt @@ -29,10 +29,6 @@ interface ApplicationApi { fun getApplicationCategoryPreference(): List - suspend fun getHomeScreenData( - authData: AuthData, - ): LiveData>> - /* * Return three elements from the function. * - List : List of categories. diff --git a/app/src/main/java/foundation/e/apps/data/application/ApplicationApiImpl.kt b/app/src/main/java/foundation/e/apps/data/application/ApplicationApiImpl.kt index bcba66eec..6db8ae067 100644 --- a/app/src/main/java/foundation/e/apps/data/application/ApplicationApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/application/ApplicationApiImpl.kt @@ -20,8 +20,6 @@ package foundation.e.apps.data.application import android.content.Context import android.text.format.Formatter -import androidx.lifecycle.LiveData -import androidx.lifecycle.liveData import com.aurora.gplayapi.Constants import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App @@ -32,28 +30,26 @@ import com.aurora.gplayapi.data.models.StreamCluster import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme +import foundation.e.apps.data.application.ApplicationApi.Companion.APP_TYPE_ANY +import foundation.e.apps.data.application.ApplicationApi.Companion.APP_TYPE_OPEN +import foundation.e.apps.data.application.ApplicationApi.Companion.APP_TYPE_PWA +import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.application.data.Category +import foundation.e.apps.data.application.data.Home +import foundation.e.apps.data.application.data.Ratings +import foundation.e.apps.data.application.utils.CategoryType +import foundation.e.apps.data.application.utils.CategoryUtils import foundation.e.apps.data.cleanapk.CleanApkDownloadInfoFetcher import foundation.e.apps.data.cleanapk.data.categories.Categories -import foundation.e.apps.data.cleanapk.data.home.HomeScreen import foundation.e.apps.data.cleanapk.data.search.Search import foundation.e.apps.data.cleanapk.repositories.CleanApkRepository import foundation.e.apps.data.enums.AppTag import foundation.e.apps.data.enums.FilterLevel import foundation.e.apps.data.enums.Origin 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.Type import foundation.e.apps.data.enums.isUnFiltered -import foundation.e.apps.data.application.ApplicationApi.Companion.APP_TYPE_ANY -import foundation.e.apps.data.application.ApplicationApi.Companion.APP_TYPE_OPEN -import foundation.e.apps.data.application.ApplicationApi.Companion.APP_TYPE_PWA -import foundation.e.apps.data.application.data.Application -import foundation.e.apps.data.application.data.Category -import foundation.e.apps.data.application.data.Home -import foundation.e.apps.data.application.data.Ratings -import foundation.e.apps.data.application.utils.CategoryType -import foundation.e.apps.data.application.utils.CategoryUtils import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.data.login.AuthObject @@ -66,8 +62,6 @@ import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.Deferred import kotlinx.coroutines.MainScope -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import retrofit2.Response import timber.log.Timber @@ -76,7 +70,6 @@ import javax.inject.Named import javax.inject.Singleton import com.aurora.gplayapi.data.models.Category as GplayapiCategory import foundation.e.apps.data.cleanapk.data.app.Application as CleanApkApplication -import foundation.e.apps.data.cleanapk.data.home.Home as CleanApkHome typealias FusedHomeDeferred = Deferred>> @@ -95,7 +88,6 @@ class ApplicationApiImpl @Inject constructor( private const val CATEGORY_TITLE_REPLACEABLE_CONJUNCTION = "&" private const val CATEGORY_OPEN_GAMES_ID = "game_open_games" private const val CATEGORY_OPEN_GAMES_TITLE = "Open games" - private const val THRESHOLD_LIMITED_RESULT_HOME_PAGE = 4 private const val KEYWORD_TEST_SEARCH = "facebook" } @@ -107,97 +99,6 @@ class ApplicationApiImpl @Inject constructor( return prefs } - override suspend fun getHomeScreenData( - authData: AuthData, - ): LiveData>> { - - val list = mutableListOf() - var resultGplay: FusedHomeDeferred? = null - var resultOpenSource: FusedHomeDeferred? = null - var resultPWA: FusedHomeDeferred? = null - - return liveData { - coroutineScope { - - if (preferenceManagerModule.isGplaySelected()) { - resultGplay = async { loadHomeData(list, Source.GPLAY, authData) } - } - - if (preferenceManagerModule.isOpenSourceSelected()) { - resultOpenSource = async { loadHomeData(list, Source.OPEN, authData) } - } - - if (preferenceManagerModule.isPWASelected()) { - resultPWA = async { loadHomeData(list, Source.PWA, authData) } - } - - resultGplay?.await()?.let { - emit(it) - } - - resultOpenSource?.await()?.let { - emit(it) - } - - resultPWA?.await()?.let { - emit(it) - } - } - } - } - - private suspend fun loadHomeData( - priorList: MutableList, - source: Source, - authData: AuthData, - ): ResultSupreme> { - - val result = when (source) { - Source.GPLAY -> handleNetworkResult> { - priorList.addAll(fetchGPlayHome(authData)) - priorList - } - - Source.OPEN -> handleNetworkResult { - val response = - (cleanApkAppsRepository.getHomeScreenData() as Response).body() - response?.home?.let { - priorList.addAll(generateCleanAPKHome(it, APP_TYPE_OPEN)) - } - priorList - } - - Source.PWA -> handleNetworkResult { - val response = - (cleanApkPWARepository.getHomeScreenData() as Response).body() - response?.home?.let { - priorList.addAll(generateCleanAPKHome(it, APP_TYPE_PWA)) - } - priorList - } - } - - setHomeErrorMessage(result.getResultStatus(), source) - priorList.sortByDescending { - when (it.source) { - APP_TYPE_OPEN -> 2 - APP_TYPE_PWA -> 1 - else -> 3 - } - } - return ResultSupreme.create(result.getResultStatus(), priorList) - } - - private fun setHomeErrorMessage(apiStatus: ResultStatus, source: Source) { - if (apiStatus != ResultStatus.OK) { - apiStatus.message = when (source) { - Source.GPLAY -> ("GPlay home loading error\n" + apiStatus.message).trim() - Source.OPEN -> ("Open Source home loading error\n" + apiStatus.message).trim() - Source.PWA -> ("PWA home loading error\n" + apiStatus.message).trim() - } - } - } - /* * Return three elements from the function. * - List : List of categories. @@ -756,7 +657,7 @@ class ApplicationApiImpl @Inject constructor( /* * Handy method to run on an instance of FusedApp to update its filter level. */ - private suspend fun Application.updateFilterLevel(authData: AuthData?) { + suspend fun Application.updateFilterLevel(authData: AuthData?) { this.filterLevel = getAppFilterLevel(this, authData) } @@ -1065,111 +966,6 @@ class ApplicationApiImpl @Inject constructor( return gPlayFusedApp } - /* - * Home screen-related internal functions - */ - - private suspend fun generateCleanAPKHome(home: CleanApkHome, appType: String): List { - val list = mutableListOf() - val headings = if (appType == APP_TYPE_OPEN) { - getOpenSourceHomeCategories() - } else { - getPWAHomeCategories() - } - headings.forEach { (key, value) -> - when (key) { - "top_updated_apps" -> { - prepareApps(home.top_updated_apps, list, value) - } - - "top_updated_games" -> { - prepareApps(home.top_updated_games, list, value) - } - - "popular_apps" -> { - prepareApps(home.popular_apps, list, value) - } - - "popular_games" -> { - prepareApps(home.popular_games, list, value) - } - - "popular_apps_in_last_24_hours" -> { - prepareApps(home.popular_apps_in_last_24_hours, list, value) - } - - "popular_games_in_last_24_hours" -> { - prepareApps(home.popular_games_in_last_24_hours, list, value) - } - - "discover" -> { - prepareApps(home.discover, list, value) - } - } - } - - return list.map { - it.source = appType - it - } - } - - private suspend fun prepareApps( - appList: List, - list: MutableList, - value: String - ) { - if (appList.isNotEmpty()) { - appList.forEach { - it.updateStatus() - it.updateType() - it.updateFilterLevel(null) - } - list.add(Home(value, appList)) - } - } - - private fun getPWAHomeCategories() = mapOf( - "popular_apps" to context.getString(R.string.popular_apps), - "popular_games" to context.getString(R.string.popular_games), - "discover" to context.getString(R.string.discover_pwa) - ) - - private fun getOpenSourceHomeCategories() = mapOf( - "top_updated_apps" to context.getString(R.string.top_updated_apps), - "top_updated_games" to context.getString(R.string.top_updated_games), - "popular_apps_in_last_24_hours" to context.getString(R.string.popular_apps_in_last_24_hours), - "popular_games_in_last_24_hours" to context.getString(R.string.popular_games_in_last_24_hours), - "discover" to context.getString(R.string.discover) - ) - - private suspend fun fetchGPlayHome(authData: AuthData): List { - val list = mutableListOf() - val gplayHomeData = gplayRepository.getHomeScreenData() as Map> - gplayHomeData.map { - val fusedApps = it.value.map { app -> - app.transformToFusedApp().apply { - updateFilterLevel(authData) - } - } - list.add(Home(it.key, fusedApps)) - } - - handleLimitedResult(list) - Timber.d("HomePageData: $list") - - return list - } - - private fun handleLimitedResult(homeList: List) { - val gplayHomes = homeList.filter { fusedHome -> fusedHome.source.isEmpty() } - val hasGplayLimitedResult = gplayHomes.any { fusedHome -> fusedHome.list.size < THRESHOLD_LIMITED_RESULT_HOME_PAGE } - if (hasGplayLimitedResult) { - Timber.w("Limited result is found for homepage...") - refreshToken() - } - } - private fun refreshToken() { MainScope().launch { EventBus.invokeEvent( 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 new file mode 100644 index 000000000..8ef017903 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/data/application/ApplicationDataManager.kt @@ -0,0 +1,120 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * 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 . + */ + +package foundation.e.apps.data.application + +import com.aurora.gplayapi.Constants +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.application.data.Home +import foundation.e.apps.data.enums.FilterLevel +import foundation.e.apps.data.enums.Origin +import foundation.e.apps.data.enums.Status +import foundation.e.apps.data.playstore.PlayStoreRepository +import foundation.e.apps.install.pkg.PWAManagerModule +import foundation.e.apps.install.pkg.PkgManagerModule +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +@Singleton +class ApplicationDataManager @Inject constructor( + @Named("gplayRepository") private val gplayRepository: PlayStoreRepository, + private val pkgManagerModule: PkgManagerModule, + private val pwaManagerModule: PWAManagerModule +) { + suspend fun updateFilterLevel(authData: AuthData?, application: Application) { + application.filterLevel = getAppFilterLevel(application, authData) + } + + suspend fun prepareApps( + appList: List, + list: MutableList, + value: String + ) { + if (appList.isNotEmpty()) { + appList.forEach { + it.updateType() + updateStatus(it) + updateFilterLevel(null, it) + } + list.add(Home(value, appList)) + } + } + + private suspend fun getAppFilterLevel(application: Application, authData: AuthData?): FilterLevel { + return when { + application.package_name.isBlank() -> FilterLevel.UNKNOWN + !application.isFree && application.price.isBlank() -> FilterLevel.UI + application.origin == Origin.CLEANAPK -> FilterLevel.NONE + !isRestricted(application) -> FilterLevel.NONE + authData == null -> FilterLevel.UNKNOWN // cannot determine for gplay app + !isApplicationVisible(application) -> FilterLevel.DATA + application.originalSize == 0L -> FilterLevel.UI + !isDownloadable(application) -> FilterLevel.UI + else -> FilterLevel.NONE + } + } + + private fun isRestricted(application: Application): Boolean { + 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 { gplayRepository.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 { + gplayRepository.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) + } + } + + /** + * Get fused app installation status. + * Applicable for both native apps and PWAs. + * + * Recommended to use this instead of [PkgManagerModule.getPackageStatus]. + */ + fun getFusedAppInstallationStatus(application: Application): Status { + return if (application.is_pwa) { + pwaManagerModule.getPwaStatus(application) + } else { + pkgManagerModule.getPackageStatus(application.package_name, application.latest_version_code) + } + } + +} diff --git a/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt b/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt index ab7d541a8..d38d16634 100644 --- a/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt @@ -37,10 +37,13 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class ApplicationRepository @Inject constructor(private val applicationAPIImpl: ApplicationApi) { +class ApplicationRepository @Inject constructor( + private val applicationAPIImpl: ApplicationApi, + private val homeApi: HomeApi +) { suspend fun getHomeScreenData(authData: AuthData): LiveData>> { - return applicationAPIImpl.getHomeScreenData(authData) + return homeApi.getHomeScreenData(authData) } fun getApplicationCategoryPreference(): List { diff --git a/app/src/main/java/foundation/e/apps/data/application/HomeApi.kt b/app/src/main/java/foundation/e/apps/data/application/HomeApi.kt new file mode 100644 index 000000000..d6c860e46 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/data/application/HomeApi.kt @@ -0,0 +1,30 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * 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 . + */ + +package foundation.e.apps.data.application + +import androidx.lifecycle.LiveData +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.data.ResultSupreme +import foundation.e.apps.data.application.data.Home + +interface HomeApi { + suspend fun getHomeScreenData( + authData: AuthData, + ): LiveData>> +} diff --git a/app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt b/app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt new file mode 100644 index 000000000..cf5c6d6ed --- /dev/null +++ b/app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt @@ -0,0 +1,258 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * 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 . + */ + +package foundation.e.apps.data.application + +import android.content.Context +import androidx.lifecycle.LiveData +import androidx.lifecycle.liveData +import com.aurora.gplayapi.data.models.App +import com.aurora.gplayapi.data.models.AuthData +import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.R +import foundation.e.apps.data.ResultSupreme +import foundation.e.apps.data.application.data.Home +import foundation.e.apps.data.application.utils.transformToFusedApp +import foundation.e.apps.data.cleanapk.data.home.HomeScreen +import foundation.e.apps.data.cleanapk.repositories.CleanApkRepository +import foundation.e.apps.data.enums.ResultStatus +import foundation.e.apps.data.enums.Source +import foundation.e.apps.data.handleNetworkResult +import foundation.e.apps.data.login.AuthObject +import foundation.e.apps.data.playstore.PlayStoreRepository +import foundation.e.apps.data.preference.PreferenceManagerModule +import foundation.e.apps.utils.eventBus.AppEvent +import foundation.e.apps.utils.eventBus.EventBus +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import retrofit2.Response +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Named +import foundation.e.apps.data.cleanapk.data.home.Home as CleanApkHome + +class HomeApiImpl @Inject constructor( + @ApplicationContext private val context: Context, + private val preferenceManagerModule: PreferenceManagerModule, + @Named("gplayRepository") private val gplayRepository: PlayStoreRepository, + @Named("cleanApkAppsRepository") private val cleanApkAppsRepository: CleanApkRepository, + @Named("cleanApkPWARepository") private val cleanApkPWARepository: CleanApkRepository, + private val applicationDataManager: ApplicationDataManager +) : HomeApi { + + companion object { + private const val THRESHOLD_LIMITED_RESULT_HOME_PAGE = 4 + private const val WEIGHT_PWA = 1 + private const val WEIGHT_OPEN_SOURCE = 2 + private const val WEIGHT_GPLAY = 3 + } + override suspend fun getHomeScreenData(authData: AuthData): LiveData>> { + val list = mutableListOf() + var resultGplay: FusedHomeDeferred? = null + var resultOpenSource: FusedHomeDeferred? = null + var resultPWA: FusedHomeDeferred? = null + + return liveData { + coroutineScope { + + if (preferenceManagerModule.isGplaySelected()) { + resultGplay = async { loadHomeData(list, Source.GPLAY, authData) } + } + + if (preferenceManagerModule.isOpenSourceSelected()) { + resultOpenSource = async { loadHomeData(list, Source.OPEN, authData) } + } + + if (preferenceManagerModule.isPWASelected()) { + resultPWA = async { loadHomeData(list, Source.PWA, authData) } + } + + resultGplay?.await()?.let { + emit(it) + } + + resultOpenSource?.await()?.let { + emit(it) + } + + resultPWA?.await()?.let { + emit(it) + } + } + } + } + + private suspend fun loadHomeData( + priorList: MutableList, + source: Source, + authData: AuthData, + ): ResultSupreme> { + + val result = when (source) { + Source.GPLAY -> handleNetworkResult { + fetchGPlayHome(authData, priorList) + } + + Source.OPEN -> handleNetworkResult { + handleCleanApkHomes(priorList, ApplicationApi.APP_TYPE_OPEN) + } + + Source.PWA -> handleNetworkResult { + handleCleanApkHomes(priorList, ApplicationApi.APP_TYPE_PWA) + } + } + + setHomeErrorMessage(result.getResultStatus(), source) + priorList.sortByDescending { + when (it.source) { + ApplicationApi.APP_TYPE_OPEN -> WEIGHT_OPEN_SOURCE + ApplicationApi.APP_TYPE_PWA -> WEIGHT_PWA + else -> WEIGHT_GPLAY + } + } + + return ResultSupreme.create(result.getResultStatus(), priorList) + } + + private suspend fun handleCleanApkHomes(priorList: MutableList, appType: String): MutableList { + val response = if (appType == ApplicationApi.APP_TYPE_OPEN) { + (cleanApkAppsRepository.getHomeScreenData() as Response).body() + } else { + (cleanApkPWARepository.getHomeScreenData() as Response).body() + } + + response?.home?.let { + priorList.addAll(generateCleanAPKHome(it, appType)) + } + + return priorList + } + + private suspend fun generateCleanAPKHome(home: CleanApkHome, appType: String): List { + val list = mutableListOf() + val headings = if (appType == ApplicationApi.APP_TYPE_OPEN) { + getOpenSourceHomeCategories() + } else { + getPWAHomeCategories() + } + + headings.forEach { (key, value) -> + when (key) { + "top_updated_apps" -> { + applicationDataManager.prepareApps(home.top_updated_apps, list, value) + } + + "top_updated_games" -> { + applicationDataManager.prepareApps(home.top_updated_games, list, value) + } + + "popular_apps" -> { + applicationDataManager.prepareApps(home.popular_apps, list, value) + } + + "popular_games" -> { + applicationDataManager.prepareApps(home.popular_games, list, value) + } + + "popular_apps_in_last_24_hours" -> { + applicationDataManager.prepareApps(home.popular_apps_in_last_24_hours, list, value) + } + + "popular_games_in_last_24_hours" -> { + applicationDataManager.prepareApps(home.popular_games_in_last_24_hours, list, value) + } + + "discover" -> { + applicationDataManager.prepareApps(home.discover, list, value) + } + } + } + + return list.map { + it.source = appType + it + } + } + + private fun getPWAHomeCategories() = mapOf( + "popular_apps" to context.getString(R.string.popular_apps), + "popular_games" to context.getString(R.string.popular_games), + "discover" to context.getString(R.string.discover_pwa) + ) + + private fun getOpenSourceHomeCategories() = mapOf( + "top_updated_apps" to context.getString(R.string.top_updated_apps), + "top_updated_games" to context.getString(R.string.top_updated_games), + "popular_apps_in_last_24_hours" to context.getString(R.string.popular_apps_in_last_24_hours), + "popular_games_in_last_24_hours" to context.getString(R.string.popular_games_in_last_24_hours), + "discover" to context.getString(R.string.discover) + ) + + private fun setHomeErrorMessage(apiStatus: ResultStatus, source: Source) { + if (apiStatus != ResultStatus.OK) { + apiStatus.message = when (source) { + Source.GPLAY -> ("GPlay home loading error\n" + apiStatus.message).trim() + Source.OPEN -> ("Open Source home loading error\n" + apiStatus.message).trim() + Source.PWA -> ("PWA home loading error\n" + apiStatus.message).trim() + } + } + } + + private suspend fun fetchGPlayHome(authData: AuthData, priorList: MutableList): List { + val list = mutableListOf() + val gplayHomeData = gplayRepository.getHomeScreenData() as Map> + gplayHomeData.map { + val fusedApps = it.value.map { app -> + app.transformToFusedApp (context).apply { + applicationDataManager.updateStatus(this) + applicationDataManager.updateFilterLevel(authData, this) + } + } + list.add(Home(it.key, fusedApps)) + } + + handleLimitedResult(list) + Timber.d("HomePageData: $list") + + priorList.addAll(list) + return priorList + } + + private fun handleLimitedResult(homeList: List) { + val gplayHomes = homeList.filter { fusedHome -> fusedHome.source.isEmpty() } + val hasGplayLimitedResult = gplayHomes.any { + fusedHome -> fusedHome.list.size < THRESHOLD_LIMITED_RESULT_HOME_PAGE + } + + if (hasGplayLimitedResult) { + Timber.w("Limited result is found for homepage...") + refreshToken() + } + } + + private fun refreshToken() { + MainScope().launch { + EventBus.invokeEvent( + AppEvent.InvalidAuthEvent(AuthObject.GPlayAuth::class.java.simpleName) + ) + } + } + +} diff --git a/app/src/main/java/foundation/e/apps/data/application/data/Application.kt b/app/src/main/java/foundation/e/apps/data/application/data/Application.kt index d8a544060..7b42d7e71 100644 --- a/app/src/main/java/foundation/e/apps/data/application/data/Application.kt +++ b/app/src/main/java/foundation/e/apps/data/application/data/Application.kt @@ -93,4 +93,8 @@ data class Application( */ var filterLevel: FilterLevel = FilterLevel.UNKNOWN, var isGplayReplaced: Boolean = false -) +) { + fun updateType() { + this.type = if (this.is_pwa) Type.PWA else Type.NATIVE + } +} diff --git a/app/src/main/java/foundation/e/apps/data/application/utils/Extensions.kt b/app/src/main/java/foundation/e/apps/data/application/utils/Extensions.kt new file mode 100644 index 000000000..aac5c1598 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/data/application/utils/Extensions.kt @@ -0,0 +1,69 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * 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 . + */ + +package foundation.e.apps.data.application.utils + +import android.content.Context +import android.text.format.Formatter +import com.aurora.gplayapi.data.models.App +import com.aurora.gplayapi.data.models.Artwork +import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.application.data.Ratings +import foundation.e.apps.data.enums.Origin + +fun App.transformToFusedApp(context: Context): Application { + val app = Application( + _id = this.id.toString(), + author = this.developerName, + category = this.categoryName, + description = this.description, + perms = this.permissions, + icon_image_path = this.iconArtwork.url, + last_modified = this.updatedOn, + latest_version_code = this.versionCode, + latest_version_number = this.versionName, + name = this.displayName, + other_images_path = this.screenshots.transformToList(), + package_name = this.packageName, + ratings = Ratings( + usageQualityScore = + this.labeledRating.run { + if (isNotEmpty()) { + this.replace(",", ".").toDoubleOrNull() ?: -1.0 + } else -1.0 + } + ), + offer_type = this.offerType, + origin = Origin.GPLAY, + shareUrl = this.shareUrl, + originalSize = this.size, + appSize = Formatter.formatFileSize(context, this.size), + isFree = this.isFree, + price = this.price, + restriction = this.restriction, + ) + return app +} + +private fun MutableList.transformToList(): List { + val list = mutableListOf() + this.forEach { + list.add(it.url) + } + return list +} diff --git a/app/src/main/java/foundation/e/apps/di/DataModule.kt b/app/src/main/java/foundation/e/apps/di/DataModule.kt new file mode 100644 index 000000000..3c8b3ea87 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/di/DataModule.kt @@ -0,0 +1,18 @@ +package foundation.e.apps.di + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import foundation.e.apps.data.application.HomeApi +import foundation.e.apps.data.application.HomeApiImpl +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +interface DataModule { + + @Singleton + @Binds + fun getHomeApi(homeApiImpl: HomeApiImpl): HomeApi +} -- GitLab From e2918ba837b69091a100cfeea48e142b04eb1fa3 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Fri, 24 Nov 2023 10:55:57 +0600 Subject: [PATCH 2/3] fixed: unit test for HomeApi --- .../data/application/ApplicationRepository.kt | 2 +- .../e/apps/data/application/HomeApi.kt | 2 +- .../e/apps/data/application/HomeApiImpl.kt | 6 +- .../{Extensions.kt => GplayApiExtensions.kt} | 2 +- .../e/apps/fused/ApplicationApiImplTest.kt | 31 ---- .../fused/ApplicationApiRepositoryTest.kt | 5 +- .../foundation/e/apps/home/HomeApiTest.kt | 150 ++++++++++++++++++ 7 files changed, 160 insertions(+), 38 deletions(-) rename app/src/main/java/foundation/e/apps/data/application/utils/{Extensions.kt => GplayApiExtensions.kt} (97%) create mode 100644 app/src/test/java/foundation/e/apps/home/HomeApiTest.kt diff --git a/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt b/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt index d38d16634..a7526bb12 100644 --- a/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt @@ -43,7 +43,7 @@ class ApplicationRepository @Inject constructor( ) { suspend fun getHomeScreenData(authData: AuthData): LiveData>> { - return homeApi.getHomeScreenData(authData) + return homeApi.fetchHomeScreenData(authData) } fun getApplicationCategoryPreference(): List { diff --git a/app/src/main/java/foundation/e/apps/data/application/HomeApi.kt b/app/src/main/java/foundation/e/apps/data/application/HomeApi.kt index d6c860e46..7e677be45 100644 --- a/app/src/main/java/foundation/e/apps/data/application/HomeApi.kt +++ b/app/src/main/java/foundation/e/apps/data/application/HomeApi.kt @@ -24,7 +24,7 @@ import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.application.data.Home interface HomeApi { - suspend fun getHomeScreenData( + suspend fun fetchHomeScreenData( authData: AuthData, ): LiveData>> } diff --git a/app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt b/app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt index cf5c6d6ed..66a8e31e8 100644 --- a/app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt @@ -27,7 +27,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.application.data.Home -import foundation.e.apps.data.application.utils.transformToFusedApp +import foundation.e.apps.data.application.utils.transformToApplication import foundation.e.apps.data.cleanapk.data.home.HomeScreen import foundation.e.apps.data.cleanapk.repositories.CleanApkRepository import foundation.e.apps.data.enums.ResultStatus @@ -63,7 +63,7 @@ class HomeApiImpl @Inject constructor( private const val WEIGHT_OPEN_SOURCE = 2 private const val WEIGHT_GPLAY = 3 } - override suspend fun getHomeScreenData(authData: AuthData): LiveData>> { + override suspend fun fetchHomeScreenData(authData: AuthData): LiveData>> { val list = mutableListOf() var resultGplay: FusedHomeDeferred? = null var resultOpenSource: FusedHomeDeferred? = null @@ -220,7 +220,7 @@ class HomeApiImpl @Inject constructor( val gplayHomeData = gplayRepository.getHomeScreenData() as Map> gplayHomeData.map { val fusedApps = it.value.map { app -> - app.transformToFusedApp (context).apply { + app.transformToApplication (context).apply { applicationDataManager.updateStatus(this) applicationDataManager.updateFilterLevel(authData, this) } diff --git a/app/src/main/java/foundation/e/apps/data/application/utils/Extensions.kt b/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt similarity index 97% rename from app/src/main/java/foundation/e/apps/data/application/utils/Extensions.kt rename to app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt index aac5c1598..1d4048242 100644 --- a/app/src/main/java/foundation/e/apps/data/application/utils/Extensions.kt +++ b/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt @@ -26,7 +26,7 @@ import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.application.data.Ratings import foundation.e.apps.data.enums.Origin -fun App.transformToFusedApp(context: Context): Application { +fun App.transformToApplication(context: Context): Application { val app = Application( _id = this.id.toString(), author = this.developerName, diff --git a/app/src/test/java/foundation/e/apps/fused/ApplicationApiImplTest.kt b/app/src/test/java/foundation/e/apps/fused/ApplicationApiImplTest.kt index 165af4f72..869e8851e 100644 --- a/app/src/test/java/foundation/e/apps/fused/ApplicationApiImplTest.kt +++ b/app/src/test/java/foundation/e/apps/fused/ApplicationApiImplTest.kt @@ -756,37 +756,6 @@ class ApplicationApiImplTest { assertEquals("getSearchResult", 4, size) } - @Test - fun testHomeScreenDataWhenDataIsLimited() = runTest { - val newAppList = mutableListOf( - App("foundation.e.demoone"), - App("foundation.e.demotwo"), - App("foundation.e.demothree"), - ) - - var newHomeData = mapOf>(Pair("Top Free Apps", newAppList)) - preferenceManagerModule.isGplaySelectedFake = true - - formatterMocked.`when` { Formatter.formatFileSize(any(), any()) }.thenReturn("15MB") - Mockito.`when`(gPlayAPIRepository.getHomeScreenData()).thenReturn(newHomeData) - Mockito.`when`(gPlayAPIRepository.getAppDetails(anyString())).thenReturn(App("foundation.e.demothree")) - Mockito.`when`(gPlayAPIRepository.getDownloadInfo(anyString(), any(), any())).thenReturn(listOf()) - Mockito.`when`(pkgManagerModule.getPackageStatus(any(), any())).thenReturn(Status.UNAVAILABLE) - - var hasLimitedDataFound = false - val job = launch { - EventBus.events.collect { - hasLimitedDataFound = true - } - } - - fusedAPIImpl.getHomeScreenData(AUTH_DATA).getOrAwaitValue() - delay(500) - job.cancel() - - assert(hasLimitedDataFound) - } - @Test fun testSearchResultWhenDataIsLimited() = runTest { preferenceManagerModule.isGplaySelectedFake = true diff --git a/app/src/test/java/foundation/e/apps/fused/ApplicationApiRepositoryTest.kt b/app/src/test/java/foundation/e/apps/fused/ApplicationApiRepositoryTest.kt index bde38b217..88e5ff2a9 100644 --- a/app/src/test/java/foundation/e/apps/fused/ApplicationApiRepositoryTest.kt +++ b/app/src/test/java/foundation/e/apps/fused/ApplicationApiRepositoryTest.kt @@ -19,6 +19,7 @@ package foundation.e.apps.fused import foundation.e.apps.data.application.ApplicationRepository import foundation.e.apps.data.application.ApplicationApiImpl +import foundation.e.apps.data.application.HomeApi import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -31,11 +32,13 @@ class ApplicationApiRepositoryTest { private lateinit var applicationRepository: ApplicationRepository @Mock private lateinit var fusedAPIImpl: ApplicationApiImpl + @Mock + private lateinit var homeApi: HomeApi @Before fun setup() { MockitoAnnotations.openMocks(this) - applicationRepository = ApplicationRepository(fusedAPIImpl) + applicationRepository = ApplicationRepository(fusedAPIImpl, homeApi) } @Test diff --git a/app/src/test/java/foundation/e/apps/home/HomeApiTest.kt b/app/src/test/java/foundation/e/apps/home/HomeApiTest.kt new file mode 100644 index 000000000..b32e0f941 --- /dev/null +++ b/app/src/test/java/foundation/e/apps/home/HomeApiTest.kt @@ -0,0 +1,150 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * 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 . + */ + +package foundation.e.apps.home + +import android.content.Context +import android.text.format.Formatter +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import com.aurora.gplayapi.data.models.App +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.FakePreferenceModule +import foundation.e.apps.data.application.ApplicationDataManager +import foundation.e.apps.data.application.HomeApi +import foundation.e.apps.data.application.HomeApiImpl +import foundation.e.apps.data.cleanapk.repositories.CleanApkRepository +import foundation.e.apps.data.enums.Status +import foundation.e.apps.data.playstore.PlayStoreRepository +import foundation.e.apps.install.pkg.PWAManagerModule +import foundation.e.apps.install.pkg.PkgManagerModule +import foundation.e.apps.util.MainCoroutineRule +import foundation.e.apps.util.getOrAwaitValue +import foundation.e.apps.utils.eventBus.EventBus +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.ArgumentMatchers +import org.mockito.Mock +import org.mockito.MockedStatic +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any + +class HomeApiTest { + + // Run tasks synchronously + @Rule + @JvmField + val instantExecutorRule = InstantTaskExecutorRule() + + // Sets the main coroutines dispatcher to a TestCoroutineScope for unit testing. + @ExperimentalCoroutinesApi + @get:Rule + var mainCoroutineRule = MainCoroutineRule() + + private lateinit var homeApi: HomeApi + + private lateinit var applicationDataManager: ApplicationDataManager + + @Mock + private lateinit var pwaManagerModule: PWAManagerModule + + @Mock + private lateinit var pkgManagerModule: PkgManagerModule + + @Mock + private lateinit var context: Context + + @Mock + private lateinit var cleanApkAppsRepository: CleanApkRepository + + @Mock + private lateinit var cleanApkPWARepository: CleanApkRepository + + @Mock + private lateinit var gPlayAPIRepository: PlayStoreRepository + + private lateinit var preferenceManagerModule: FakePreferenceModule + + private lateinit var formatterMocked: MockedStatic + + companion object { + private val AUTH_DATA = AuthData("e@e.email", "AtadyMsIAtadyM") + } + + @Before + fun setup() { + MockitoAnnotations.openMocks(this) + formatterMocked = Mockito.mockStatic(Formatter::class.java) + preferenceManagerModule = FakePreferenceModule(context) + applicationDataManager = + ApplicationDataManager(gPlayAPIRepository, pkgManagerModule, pwaManagerModule) + homeApi = HomeApiImpl( + context, + preferenceManagerModule, + gPlayAPIRepository, + cleanApkAppsRepository, + cleanApkPWARepository, + applicationDataManager + ) + } + + @Test + fun testHomeScreenDataWhenDataIsLimited() = runTest { + val newAppList = mutableListOf( + App("foundation.e.demoone"), + App("foundation.e.demotwo"), + App("foundation.e.demothree"), + ) + + var newHomeData = mapOf>(Pair("Top Free Apps", newAppList)) + preferenceManagerModule.isGplaySelectedFake = true + + formatterMocked.`when` { Formatter.formatFileSize(any(), any()) }.thenReturn("15MB") + Mockito.`when`(gPlayAPIRepository.getHomeScreenData()).thenReturn(newHomeData) + Mockito.`when`(gPlayAPIRepository.getAppDetails(ArgumentMatchers.anyString())).thenReturn( + App("foundation.e.demothree") + ) + Mockito.`when`( + gPlayAPIRepository.getDownloadInfo( + ArgumentMatchers.anyString(), + any(), + any() + ) + ).thenReturn(listOf()) + Mockito.`when`(pkgManagerModule.getPackageStatus(any(), any())) + .thenReturn(Status.UNAVAILABLE) + + var hasLimitedDataFound = false + val job = launch { + EventBus.events.collect { + hasLimitedDataFound = true + } + } + + homeApi.fetchHomeScreenData(AUTH_DATA).getOrAwaitValue() + delay(500) + job.cancel() + + assert(hasLimitedDataFound) + } +} \ No newline at end of file -- GitLab From 9f7845b183da23b28755c64e1259c6357b425e95 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Mon, 27 Nov 2023 17:13:53 +0600 Subject: [PATCH 3/3] Enum is introduced for app sources --- app/detekt-baseline.xml | 8 ----- .../application/ApplicationDataManager.kt | 6 ++-- .../e/apps/data/application/HomeApiImpl.kt | 36 ++++++++++++------- .../application/utils/GplayApiExtensions.kt | 6 ++-- .../java/foundation/e/apps/di/DataModule.kt | 18 ++++++++++ 5 files changed, 47 insertions(+), 27 deletions(-) diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml index c9feddb68..7813ffbf5 100644 --- a/app/detekt-baseline.xml +++ b/app/detekt-baseline.xml @@ -28,7 +28,6 @@ MagicNumber:ApkSignatureManager.kt$ApkSignatureManager$1024 MagicNumber:AppDatabase.kt$AppDatabase.Companion.<no name provided>$3 MagicNumber:AppDatabase.kt$AppDatabase.Companion.<no name provided>$4 - MagicNumber:ApplicationApiImpl.kt$ApplicationApiImpl$3 MagicNumber:ApplicationFragment.kt$ApplicationFragment$100f MagicNumber:ApplicationFragment.kt$ApplicationFragment$15 MagicNumber:ApplicationFragment.kt$ApplicationFragment$3 @@ -74,8 +73,6 @@ MaxLineLength:AppPrivacyInfo.kt$AppPrivacyInfo MaxLineLength:ApplicationApi.kt$ApplicationApi$suspend fun getGplayAppsByCategory(authData: AuthData, category: String, pageUrl: String?): ResultSupreme<Pair<List<Application>, String>> MaxLineLength:ApplicationApiImpl.kt$ApplicationApiImpl$(cleanApkAppsRepository.getAppDetails(result.apps[0]._id) as Response<CleanApkApplication>).body()?.app - MaxLineLength:ApplicationApiImpl.kt$ApplicationApiImpl$val hasGplayLimitedResult = gplayHomes.any { fusedHome -> fusedHome.list.size < THRESHOLD_LIMITED_RESULT_HOME_PAGE } - MaxLineLength:ApplicationDiffUtil.kt$ApplicationDiffUtil$((oldItem.trackers == LIST_OF_NULL && newItem.trackers.isEmpty()) || oldItem.trackers == newItem.trackers) MaxLineLength:ApplicationFragment.kt$ApplicationFragment.Companion$"https://gitlab.e.foundation/e/os/apps/-/blob/main/app/src/main/java/foundation/e/apps/data/exodus/repositories/PrivacyScoreRepositoryImpl.kt" MaxLineLength:CommonUtilsModule.kt$CommonUtilsModule$* MaxLineLength:DownloadManager.kt$DownloadManager$Timber.e("Download Issue: $downloadId : DownloadManager returns status: $status but the failed because: reason: $reason") @@ -83,9 +80,6 @@ MaxLineLength:DownloadManagerUtils.kt$DownloadManagerUtils$Timber.d("===> updateDownloadStatus: ${fusedDownload.name}: $downloadId: $numberOfDownloadedItems/${fusedDownload.downloadIdMap.size}") MaxLineLength:DownloadManagerUtils.kt$DownloadManagerUtils$if MaxLineLength:DownloadManagerUtils.kt$DownloadManagerUtils$numberOfDownloadedItems == fusedDownload.downloadIdMap.size && numberOfDownloadedItems == fusedDownload.downloadURLList.size - MaxLineLength:DownloadProgressLD.kt$DownloadProgressLD$cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)) - MaxLineLength:DownloadProgressLD.kt$DownloadProgressLD$cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)) - MaxLineLength:DownloadProgressLD.kt$DownloadProgressLD$status == DownloadManager.STATUS_SUCCESSFUL || status == DownloadManager.STATUS_FAILED MaxLineLength:FdroidRepository.kt$FdroidRepository$override suspend MaxLineLength:FusedManagerImpl.kt$FusedManagerImpl$. MaxLineLength:FusedManagerImpl.kt$FusedManagerImpl$return @@ -120,8 +114,6 @@ ReturnCount:AppPrivacyInfoRepositoryImpl.kt$AppPrivacyInfoRepositoryImpl$override suspend fun getAppPrivacyInfo( application: Application, appHandle: String ): Result<AppPrivacyInfo> ReturnCount:AppPrivacyInfoRepositoryImpl.kt$AppPrivacyInfoRepositoryImpl$private fun getAppPrivacyInfo( application: Application, appTrackerData: List<Report>, ): AppPrivacyInfo ReturnCount:ApplicationApiImpl.kt$ApplicationApiImpl$override fun isAnyFusedAppUpdated( newApplications: List<Application>, oldApplications: List<Application> ): Boolean - ReturnCount:ApplicationApiImpl.kt$ApplicationApiImpl$override fun isHomeDataUpdated( newHomeData: List<Home>, oldHomeData: List<Home> ): Boolean - ReturnCount:ApplicationApiImpl.kt$ApplicationApiImpl$private fun areFusedAppsUpdated( oldHome: Home, newHome: Home, ): Boolean ReturnCount:DownloadManager.kt$DownloadManager$fun getSizeRequired(downloadId: Long): Long ReturnCount:DownloadManager.kt$DownloadManager$private fun sanitizeStatus(downloadId: Long, status: Int, reason: Int): Int ReturnCount:Extensions.kt$fun Context.isNetworkAvailable(): Boolean 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 8ef017903..a537d6bf7 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 @@ -75,7 +75,7 @@ class ApplicationDataManager @Inject constructor( return application.restriction != Constants.Restriction.NOT_RESTRICTED } - /** + /* * Some apps are simply not visible. * Example: com.skype.m2 */ @@ -83,7 +83,7 @@ class ApplicationDataManager @Inject constructor( return kotlin.runCatching { gplayRepository.getAppDetails(application.package_name) }.isSuccess } - /** + /* * Some apps are visible but not downloadable. * Example: com.riotgames.league.wildrift */ @@ -103,7 +103,7 @@ class ApplicationDataManager @Inject constructor( } } - /** + /* * Get fused app installation status. * Applicable for both native apps and PWAs. * diff --git a/app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt b/app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt index 66a8e31e8..9bc92f99d 100644 --- a/app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/application/HomeApiImpl.kt @@ -27,7 +27,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.application.data.Home -import foundation.e.apps.data.application.utils.transformToApplication +import foundation.e.apps.data.application.utils.toApplication import foundation.e.apps.data.cleanapk.data.home.HomeScreen import foundation.e.apps.data.cleanapk.repositories.CleanApkRepository import foundation.e.apps.data.enums.ResultStatus @@ -59,10 +59,14 @@ class HomeApiImpl @Inject constructor( companion object { private const val THRESHOLD_LIMITED_RESULT_HOME_PAGE = 4 - private const val WEIGHT_PWA = 1 - private const val WEIGHT_OPEN_SOURCE = 2 - private const val WEIGHT_GPLAY = 3 } + + private enum class AppSourceWeight { + GPLAY, + OPEN_SOURCE, + PWA + } + override suspend fun fetchHomeScreenData(authData: AuthData): LiveData>> { val list = mutableListOf() var resultGplay: FusedHomeDeferred? = null @@ -120,18 +124,21 @@ class HomeApiImpl @Inject constructor( } setHomeErrorMessage(result.getResultStatus(), source) - priorList.sortByDescending { + priorList.sortBy { when (it.source) { - ApplicationApi.APP_TYPE_OPEN -> WEIGHT_OPEN_SOURCE - ApplicationApi.APP_TYPE_PWA -> WEIGHT_PWA - else -> WEIGHT_GPLAY + ApplicationApi.APP_TYPE_OPEN -> AppSourceWeight.OPEN_SOURCE.ordinal + ApplicationApi.APP_TYPE_PWA -> AppSourceWeight.PWA.ordinal + else -> AppSourceWeight.GPLAY.ordinal } } return ResultSupreme.create(result.getResultStatus(), priorList) } - private suspend fun handleCleanApkHomes(priorList: MutableList, appType: String): MutableList { + private suspend fun handleCleanApkHomes( + priorList: MutableList, + appType: String + ): MutableList { val response = if (appType == ApplicationApi.APP_TYPE_OPEN) { (cleanApkAppsRepository.getHomeScreenData() as Response).body() } else { @@ -215,12 +222,15 @@ class HomeApiImpl @Inject constructor( } } - private suspend fun fetchGPlayHome(authData: AuthData, priorList: MutableList): List { + private suspend fun fetchGPlayHome( + authData: AuthData, + priorList: MutableList + ): List { val list = mutableListOf() val gplayHomeData = gplayRepository.getHomeScreenData() as Map> gplayHomeData.map { val fusedApps = it.value.map { app -> - app.transformToApplication (context).apply { + app.toApplication(context).apply { applicationDataManager.updateStatus(this) applicationDataManager.updateFilterLevel(authData, this) } @@ -237,8 +247,8 @@ class HomeApiImpl @Inject constructor( private fun handleLimitedResult(homeList: List) { val gplayHomes = homeList.filter { fusedHome -> fusedHome.source.isEmpty() } - val hasGplayLimitedResult = gplayHomes.any { - fusedHome -> fusedHome.list.size < THRESHOLD_LIMITED_RESULT_HOME_PAGE + val hasGplayLimitedResult = gplayHomes.any { fusedHome -> + fusedHome.list.size < THRESHOLD_LIMITED_RESULT_HOME_PAGE } if (hasGplayLimitedResult) { diff --git a/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt b/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt index 1d4048242..88cdb6451 100644 --- a/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt +++ b/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt @@ -26,7 +26,7 @@ import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.application.data.Ratings import foundation.e.apps.data.enums.Origin -fun App.transformToApplication(context: Context): Application { +fun App.toApplication(context: Context): Application { val app = Application( _id = this.id.toString(), author = this.developerName, @@ -38,7 +38,7 @@ fun App.transformToApplication(context: Context): Application { latest_version_code = this.versionCode, latest_version_number = this.versionName, name = this.displayName, - other_images_path = this.screenshots.transformToList(), + other_images_path = this.screenshots.toList(), package_name = this.packageName, ratings = Ratings( usageQualityScore = @@ -60,7 +60,7 @@ fun App.transformToApplication(context: Context): Application { return app } -private fun MutableList.transformToList(): List { +private fun MutableList.toList(): List { val list = mutableListOf() this.forEach { list.add(it.url) diff --git a/app/src/main/java/foundation/e/apps/di/DataModule.kt b/app/src/main/java/foundation/e/apps/di/DataModule.kt index 3c8b3ea87..f464ef454 100644 --- a/app/src/main/java/foundation/e/apps/di/DataModule.kt +++ b/app/src/main/java/foundation/e/apps/di/DataModule.kt @@ -1,3 +1,21 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * 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 . + */ + package foundation.e.apps.di import dagger.Binds -- GitLab