Loading app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt +3 −3 Original line number Diff line number Diff line Loading @@ -100,7 +100,7 @@ class ApplicationRepository @Inject constructor( suspend fun getCategoriesList( type: CategoryType, ): Triple<List<Category>, String, ResultStatus> { ): Pair<List<Category>, ResultStatus> { return categoryApi.getCategoriesList(type) } Loading Loading @@ -129,8 +129,8 @@ class ApplicationRepository @Inject constructor( source: Source ): ResultSupreme<Pair<List<Application>, String>> { return when (source) { Source.OPEN -> categoryApi.getOpenSourceApps(category) Source.PWA -> categoryApi.getPWAApps(category) Source.OPEN -> categoryApi.getCleanApkAppsByCategory(category, Source.OPEN) Source.PWA -> categoryApi.getCleanApkAppsByCategory(category, Source.PWA) else -> categoryApi.getGplayAppsByCategory(authData, category, pageUrl) } } Loading app/src/main/java/foundation/e/apps/data/application/CategoryApi.kt +24 −4 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.application import com.aurora.gplayapi.data.models.AuthData Loading @@ -6,6 +24,7 @@ import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.application.data.Category import foundation.e.apps.data.application.utils.CategoryType import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Source interface CategoryApi { Loading @@ -22,7 +41,7 @@ interface CategoryApi { */ suspend fun getCategoriesList( type: CategoryType, ): Triple<List<Category>, String, ResultStatus> ): Pair<List<Category>, ResultStatus> suspend fun getGplayAppsByCategory( authData: AuthData, Loading @@ -30,7 +49,8 @@ interface CategoryApi { pageUrl: String? ): ResultSupreme<Pair<List<Application>, String>> suspend fun getPWAApps(category: String): ResultSupreme<Pair<List<Application>, String>> suspend fun getOpenSourceApps(category: String): ResultSupreme<Pair<List<Application>, String>> suspend fun getCleanApkAppsByCategory( category: String, source: Source ): ResultSupreme<Pair<List<Application>, String>> } app/src/main/java/foundation/e/apps/data/application/CategoryApiImpl.kt +109 −115 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.application import android.content.Context Loading @@ -17,6 +35,7 @@ import foundation.e.apps.data.cleanapk.data.categories.Categories import foundation.e.apps.data.cleanapk.repositories.CleanApkRepository import foundation.e.apps.data.enums.AppTag import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Source import foundation.e.apps.data.enums.isUnFiltered import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.data.playstore.PlayStoreRepository Loading @@ -33,12 +52,6 @@ class CategoryApiImpl @Inject constructor( private val applicationDataManager: ApplicationDataManager ) : CategoryApi { companion object { 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" } /* * Return three elements from the function. * - List<FusedCategory> : List of categories. Loading @@ -50,20 +63,12 @@ class CategoryApiImpl @Inject constructor( * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ override suspend fun getCategoriesList(type: CategoryType): Triple<List<Category>, String, ResultStatus> { override suspend fun getCategoriesList(type: CategoryType): Pair<List<Category>, ResultStatus> { val categoriesList = mutableListOf<Category>() val preferredApplicationType = preferenceManagerModule.preferredApplicationType() var apiStatus: ResultStatus = ResultStatus.OK var applicationCategoryType = preferredApplicationType var apiStatus = handleAllSourcesCategories(categoriesList, type) handleAllSourcesCategories(categoriesList, type).run { if (first != ResultStatus.OK) { apiStatus = first applicationCategoryType = second } } categoriesList.sortBy { item -> item.title.lowercase() } return Triple(categoriesList, applicationCategoryType, apiStatus) return Pair(categoriesList, apiStatus) } /* Loading @@ -79,98 +84,99 @@ class CategoryApiImpl @Inject constructor( private suspend fun handleAllSourcesCategories( categoriesList: MutableList<Category>, type: CategoryType, ): Pair<ResultStatus, String> { var apiStatus = ResultStatus.OK var errorApplicationCategory = "" ): ResultStatus { var categoryResult: ResultStatus = ResultStatus.OK if (preferenceManagerModule.isOpenSourceSelected()) { val openSourceCategoryResult = fetchOpenSourceCategories(type) categoriesList.addAll(openSourceCategoryResult.second) apiStatus = openSourceCategoryResult.first errorApplicationCategory = openSourceCategoryResult.third categoryResult = fetchCategoryResult(categoriesList, type, Source.OPEN) } if (preferenceManagerModule.isPWASelected()) { val pwaCategoriesResult = fetchPWACategories(type) categoriesList.addAll(pwaCategoriesResult.second) apiStatus = pwaCategoriesResult.first errorApplicationCategory = pwaCategoriesResult.third categoryResult = fetchCategoryResult(categoriesList, type, Source.PWA) } if (preferenceManagerModule.isGplaySelected()) { val gplayCategoryResult = fetchGplayCategories( type, ) categoriesList.addAll(gplayCategoryResult.data ?: listOf()) apiStatus = gplayCategoryResult.getResultStatus() errorApplicationCategory = ApplicationApi.APP_TYPE_ANY categoryResult = fetchCategoryResult(categoriesList, type, Source.GPLAY) } return categoryResult } private suspend fun fetchCategoryResult( categoriesList: MutableList<Category>, type: CategoryType, source: Source ): ResultStatus { val categoryResult = when (source) { Source.OPEN -> { fetchCleanApkCategories(type, Source.OPEN) } return Pair(apiStatus, errorApplicationCategory) Source.PWA -> { fetchCleanApkCategories(type, Source.PWA) } else -> { fetchGplayCategories(type) } } categoryResult.let { categoriesList.addAll(it.first) } return categoryResult.second } private suspend fun fetchGplayCategories( type: CategoryType, ): ResultSupreme<List<Category>> { ): Pair<List<Category>, ResultStatus> { val categoryList = mutableListOf<Category>() return handleNetworkResult { val result = handleNetworkResult { val playResponse = gplayRepository.getCategories(type).map { app -> val category = app.transformToFusedCategory() category.drawable = CategoryUtils.provideAppsCategoryIconResource(getCategoryIconName(category)) CategoryUtils.provideAppsCategoryIconResource( CategoryUtils.getCategoryIconName(category) ) category } categoryList.addAll(playResponse) categoryList } } private fun getCategoryIconName(category: Category): String { var categoryTitle = if (category.tag.getOperationalTag().contentEquals(AppTag.GPlay().getOperationalTag())) category.id else category.title if (categoryTitle.contains(CATEGORY_TITLE_REPLACEABLE_CONJUNCTION)) { categoryTitle = categoryTitle.replace(CATEGORY_TITLE_REPLACEABLE_CONJUNCTION, "and") } categoryTitle = categoryTitle.replace(' ', '_') return categoryTitle.lowercase() return Pair(result.data ?: listOf(), result.getResultStatus()) } private suspend fun fetchPWACategories( private suspend fun fetchCleanApkCategories( type: CategoryType, ): Triple<ResultStatus, List<Category>, String> { val fusedCategoriesList = mutableListOf<Category>() source: Source ): Pair<List<Category>, ResultStatus> { val categoryList = mutableListOf<Category>() var tag: AppTag? = null val result = handleNetworkResult { cleanApkPWARepository.getCategories().body()?.let { fusedCategoriesList.addAll( getFusedCategoryBasedOnCategoryType( it, type, AppTag.PWA(context.getString(R.string.pwa)) ) ) val categories = when (source) { Source.OPEN -> { tag = AppTag.OpenSource(context.getString(R.string.open_source)) cleanApkAppsRepository.getCategories().body() } Source.PWA -> { tag = AppTag.PWA(context.getString(R.string.pwa)) cleanApkPWARepository.getCategories().body() } return Triple(result.getResultStatus(), fusedCategoriesList, ApplicationApi.APP_TYPE_PWA) else -> null } private suspend fun fetchOpenSourceCategories( type: CategoryType, ): Triple<ResultStatus, List<Category>, String> { val categoryList = mutableListOf<Category>() val result = handleNetworkResult { cleanApkAppsRepository.getCategories().body()?.let { categoryList.addAll( getFusedCategoryBasedOnCategoryType( it, type, AppTag.OpenSource(context.getString(R.string.open_source)) ) ) categories?.let { categoryList.addAll(getFusedCategoryBasedOnCategoryType(it, type, tag!!)) } } return Triple(result.getResultStatus(), categoryList, ApplicationApi.APP_TYPE_OPEN) return Pair(categoryList, result.getResultStatus()) } private fun getFusedCategoryBasedOnCategoryType( Loading @@ -180,36 +186,15 @@ class CategoryApiImpl @Inject constructor( ): List<Category> { return when (categoryType) { CategoryType.APPLICATION -> { getCategories(categories, categories.apps, tag) CategoryUtils.getCategories(categories, categories.apps, tag) } CategoryType.GAMES -> { getCategories(categories, categories.games, tag) CategoryUtils.getCategories(categories, categories.games, tag) } } } private fun getCategories( categories: Categories, categoryNames: List<String>, tag: AppTag ) = categoryNames.map { category -> Category( id = category, title = getCategoryTitle(category, categories), drawable = CategoryUtils.provideAppsCategoryIconResource(category), tag = tag ) } private fun getCategoryTitle(category: String, categories: Categories): String { return if (category.contentEquals(CATEGORY_OPEN_GAMES_ID)) { CATEGORY_OPEN_GAMES_TITLE } else { categories.translations.getOrDefault(category, "") } } override suspend fun getGplayAppsByCategory( authData: AuthData, category: String, Loading @@ -228,14 +213,14 @@ class CategoryApiImpl @Inject constructor( } nextPageUrl = streamCluster.clusterNextPageUrl if (!nextPageUrl.isNullOrEmpty()) { if (nextPageUrl.isNotEmpty()) { applicationList.add(Application(isPlaceHolder = true)) } Pair(applicationList, nextPageUrl) } } /** /* * Filter out apps which are restricted, whose details cannot be fetched. * If an app is restricted, we do try to fetch the app details inside a * try-catch block. If that fails, we remove the app, else we keep it even Loading @@ -253,7 +238,11 @@ class CategoryApiImpl @Inject constructor( val filteredApplications = mutableListOf<Application>() return handleNetworkResult { appList.forEach { val filter = applicationDataManager.getAppFilterLevel(it.transformToApplication(context), authData) val filter = applicationDataManager.getAppFilterLevel( it.transformToApplication(context), authData ) if (filter.isUnFiltered()) { filteredApplications.add( it.transformToApplication(context).apply { Loading @@ -266,10 +255,14 @@ class CategoryApiImpl @Inject constructor( } } override suspend fun getPWAApps(category: String): ResultSupreme<Pair<List<Application>, String>> { override suspend fun getCleanApkAppsByCategory( category: String, source: Source ): ResultSupreme<Pair<List<Application>, String>> { val list = mutableListOf<Application>() val result = handleNetworkResult { val response = cleanApkPWARepository.getAppsByCategory(category).body() val response = getCleanApkAppsResponse(source, category) response?.apps?.forEach { applicationDataManager.updateStatus(it) it.updateType() Loading @@ -280,17 +273,18 @@ class CategoryApiImpl @Inject constructor( return ResultSupreme.create(result.getResultStatus(), Pair(list, "")) } override suspend fun getOpenSourceApps(category: String): ResultSupreme<Pair<List<Application>, String>> { val list = mutableListOf<Application>() val result = handleNetworkResult { val response = cleanApkAppsRepository.getAppsByCategory(category).body() response?.apps?.forEach { applicationDataManager.updateStatus(it) it.updateType() applicationDataManager.updateFilterLevel(null, it) list.add(it) private suspend fun getCleanApkAppsResponse( source: Source, category: String ) = when (source) { Source.OPEN -> { cleanApkAppsRepository.getAppsByCategory(category).body() } Source.PWA -> { cleanApkPWARepository.getAppsByCategory(category).body() } return ResultSupreme.create(result.getResultStatus(), Pair(list, "")) else -> null } } app/src/main/java/foundation/e/apps/data/application/utils/CategoryUtils.kt +44 −0 Original line number Diff line number Diff line Loading @@ -19,9 +19,17 @@ package foundation.e.apps.data.application.utils import foundation.e.apps.R import foundation.e.apps.data.application.data.Category import foundation.e.apps.data.cleanapk.data.categories.Categories import foundation.e.apps.data.enums.AppTag object CategoryUtils { private const val CATEGORY_OPEN_GAMES_ID = "game_open_games" private const val CATEGORY_OPEN_GAMES_TITLE = "Open games" private const val CATEGORY_TITLE_REPLACEABLE_CONJUNCTION = "&" private const val CATEGORY_TITLE_CONJUNCTION = "and" private val categoryIconMap = mapOf( "comics" to R.drawable.ic_cat_comics, "connectivity" to R.drawable.ic_cat_connectivity, Loading Loading @@ -102,6 +110,42 @@ object CategoryUtils { fun provideAppsCategoryIconResource(categoryId: String) = categoryIconMap[categoryId] ?: R.drawable.ic_cat_default fun getCategories( categories: Categories, categoryNames: List<String>, tag: AppTag ) = categoryNames.map { category -> Category( id = category, title = getCategoryTitle(category, categories), drawable = provideAppsCategoryIconResource(category), tag = tag ) } private fun getCategoryTitle(category: String, categories: Categories): String { return if (category.contentEquals(CATEGORY_OPEN_GAMES_ID)) { CATEGORY_OPEN_GAMES_TITLE } else { categories.translations.getOrDefault(category, "") } } fun getCategoryIconName(category: Category): String { var categoryTitle = if (category.tag.getOperationalTag().contentEquals(AppTag.GPlay().getOperationalTag())) category.id else category.title if (categoryTitle.contains(CATEGORY_TITLE_REPLACEABLE_CONJUNCTION)) { categoryTitle = categoryTitle.replace( CATEGORY_TITLE_REPLACEABLE_CONJUNCTION, CATEGORY_TITLE_CONJUNCTION ) } categoryTitle = categoryTitle.replace(' ', '_') return categoryTitle.lowercase() } } enum class CategoryType { Loading app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt +4 −4 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ class CategoriesViewModel @Inject constructor( private val applicationRepository: ApplicationRepository ) : LoadingViewModel() { val categoriesList: MutableLiveData<Triple<List<Category>, String, ResultStatus>> = val categoriesList: MutableLiveData<Pair<List<Category>, ResultStatus>> = MutableLiveData() fun loadData( Loading @@ -65,17 +65,17 @@ class CategoriesViewModel @Inject constructor( val categoriesData = applicationRepository.getCategoriesList(type) categoriesList.postValue(categoriesData) val status = categoriesData.third val status = categoriesData.second if (status != ResultStatus.OK) { val exception = if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) GPlayException( categoriesData.third == ResultStatus.TIMEOUT, categoriesData.second == ResultStatus.TIMEOUT, status.message.ifBlank { "Data load error" } ) else CleanApkException( categoriesData.third == ResultStatus.TIMEOUT, categoriesData.second == ResultStatus.TIMEOUT, status.message.ifBlank { "Data load error" } ) Loading Loading
app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt +3 −3 Original line number Diff line number Diff line Loading @@ -100,7 +100,7 @@ class ApplicationRepository @Inject constructor( suspend fun getCategoriesList( type: CategoryType, ): Triple<List<Category>, String, ResultStatus> { ): Pair<List<Category>, ResultStatus> { return categoryApi.getCategoriesList(type) } Loading Loading @@ -129,8 +129,8 @@ class ApplicationRepository @Inject constructor( source: Source ): ResultSupreme<Pair<List<Application>, String>> { return when (source) { Source.OPEN -> categoryApi.getOpenSourceApps(category) Source.PWA -> categoryApi.getPWAApps(category) Source.OPEN -> categoryApi.getCleanApkAppsByCategory(category, Source.OPEN) Source.PWA -> categoryApi.getCleanApkAppsByCategory(category, Source.PWA) else -> categoryApi.getGplayAppsByCategory(authData, category, pageUrl) } } Loading
app/src/main/java/foundation/e/apps/data/application/CategoryApi.kt +24 −4 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.application import com.aurora.gplayapi.data.models.AuthData Loading @@ -6,6 +24,7 @@ import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.application.data.Category import foundation.e.apps.data.application.utils.CategoryType import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Source interface CategoryApi { Loading @@ -22,7 +41,7 @@ interface CategoryApi { */ suspend fun getCategoriesList( type: CategoryType, ): Triple<List<Category>, String, ResultStatus> ): Pair<List<Category>, ResultStatus> suspend fun getGplayAppsByCategory( authData: AuthData, Loading @@ -30,7 +49,8 @@ interface CategoryApi { pageUrl: String? ): ResultSupreme<Pair<List<Application>, String>> suspend fun getPWAApps(category: String): ResultSupreme<Pair<List<Application>, String>> suspend fun getOpenSourceApps(category: String): ResultSupreme<Pair<List<Application>, String>> suspend fun getCleanApkAppsByCategory( category: String, source: Source ): ResultSupreme<Pair<List<Application>, String>> }
app/src/main/java/foundation/e/apps/data/application/CategoryApiImpl.kt +109 −115 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.data.application import android.content.Context Loading @@ -17,6 +35,7 @@ import foundation.e.apps.data.cleanapk.data.categories.Categories import foundation.e.apps.data.cleanapk.repositories.CleanApkRepository import foundation.e.apps.data.enums.AppTag import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Source import foundation.e.apps.data.enums.isUnFiltered import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.data.playstore.PlayStoreRepository Loading @@ -33,12 +52,6 @@ class CategoryApiImpl @Inject constructor( private val applicationDataManager: ApplicationDataManager ) : CategoryApi { companion object { 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" } /* * Return three elements from the function. * - List<FusedCategory> : List of categories. Loading @@ -50,20 +63,12 @@ class CategoryApiImpl @Inject constructor( * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ override suspend fun getCategoriesList(type: CategoryType): Triple<List<Category>, String, ResultStatus> { override suspend fun getCategoriesList(type: CategoryType): Pair<List<Category>, ResultStatus> { val categoriesList = mutableListOf<Category>() val preferredApplicationType = preferenceManagerModule.preferredApplicationType() var apiStatus: ResultStatus = ResultStatus.OK var applicationCategoryType = preferredApplicationType var apiStatus = handleAllSourcesCategories(categoriesList, type) handleAllSourcesCategories(categoriesList, type).run { if (first != ResultStatus.OK) { apiStatus = first applicationCategoryType = second } } categoriesList.sortBy { item -> item.title.lowercase() } return Triple(categoriesList, applicationCategoryType, apiStatus) return Pair(categoriesList, apiStatus) } /* Loading @@ -79,98 +84,99 @@ class CategoryApiImpl @Inject constructor( private suspend fun handleAllSourcesCategories( categoriesList: MutableList<Category>, type: CategoryType, ): Pair<ResultStatus, String> { var apiStatus = ResultStatus.OK var errorApplicationCategory = "" ): ResultStatus { var categoryResult: ResultStatus = ResultStatus.OK if (preferenceManagerModule.isOpenSourceSelected()) { val openSourceCategoryResult = fetchOpenSourceCategories(type) categoriesList.addAll(openSourceCategoryResult.second) apiStatus = openSourceCategoryResult.first errorApplicationCategory = openSourceCategoryResult.third categoryResult = fetchCategoryResult(categoriesList, type, Source.OPEN) } if (preferenceManagerModule.isPWASelected()) { val pwaCategoriesResult = fetchPWACategories(type) categoriesList.addAll(pwaCategoriesResult.second) apiStatus = pwaCategoriesResult.first errorApplicationCategory = pwaCategoriesResult.third categoryResult = fetchCategoryResult(categoriesList, type, Source.PWA) } if (preferenceManagerModule.isGplaySelected()) { val gplayCategoryResult = fetchGplayCategories( type, ) categoriesList.addAll(gplayCategoryResult.data ?: listOf()) apiStatus = gplayCategoryResult.getResultStatus() errorApplicationCategory = ApplicationApi.APP_TYPE_ANY categoryResult = fetchCategoryResult(categoriesList, type, Source.GPLAY) } return categoryResult } private suspend fun fetchCategoryResult( categoriesList: MutableList<Category>, type: CategoryType, source: Source ): ResultStatus { val categoryResult = when (source) { Source.OPEN -> { fetchCleanApkCategories(type, Source.OPEN) } return Pair(apiStatus, errorApplicationCategory) Source.PWA -> { fetchCleanApkCategories(type, Source.PWA) } else -> { fetchGplayCategories(type) } } categoryResult.let { categoriesList.addAll(it.first) } return categoryResult.second } private suspend fun fetchGplayCategories( type: CategoryType, ): ResultSupreme<List<Category>> { ): Pair<List<Category>, ResultStatus> { val categoryList = mutableListOf<Category>() return handleNetworkResult { val result = handleNetworkResult { val playResponse = gplayRepository.getCategories(type).map { app -> val category = app.transformToFusedCategory() category.drawable = CategoryUtils.provideAppsCategoryIconResource(getCategoryIconName(category)) CategoryUtils.provideAppsCategoryIconResource( CategoryUtils.getCategoryIconName(category) ) category } categoryList.addAll(playResponse) categoryList } } private fun getCategoryIconName(category: Category): String { var categoryTitle = if (category.tag.getOperationalTag().contentEquals(AppTag.GPlay().getOperationalTag())) category.id else category.title if (categoryTitle.contains(CATEGORY_TITLE_REPLACEABLE_CONJUNCTION)) { categoryTitle = categoryTitle.replace(CATEGORY_TITLE_REPLACEABLE_CONJUNCTION, "and") } categoryTitle = categoryTitle.replace(' ', '_') return categoryTitle.lowercase() return Pair(result.data ?: listOf(), result.getResultStatus()) } private suspend fun fetchPWACategories( private suspend fun fetchCleanApkCategories( type: CategoryType, ): Triple<ResultStatus, List<Category>, String> { val fusedCategoriesList = mutableListOf<Category>() source: Source ): Pair<List<Category>, ResultStatus> { val categoryList = mutableListOf<Category>() var tag: AppTag? = null val result = handleNetworkResult { cleanApkPWARepository.getCategories().body()?.let { fusedCategoriesList.addAll( getFusedCategoryBasedOnCategoryType( it, type, AppTag.PWA(context.getString(R.string.pwa)) ) ) val categories = when (source) { Source.OPEN -> { tag = AppTag.OpenSource(context.getString(R.string.open_source)) cleanApkAppsRepository.getCategories().body() } Source.PWA -> { tag = AppTag.PWA(context.getString(R.string.pwa)) cleanApkPWARepository.getCategories().body() } return Triple(result.getResultStatus(), fusedCategoriesList, ApplicationApi.APP_TYPE_PWA) else -> null } private suspend fun fetchOpenSourceCategories( type: CategoryType, ): Triple<ResultStatus, List<Category>, String> { val categoryList = mutableListOf<Category>() val result = handleNetworkResult { cleanApkAppsRepository.getCategories().body()?.let { categoryList.addAll( getFusedCategoryBasedOnCategoryType( it, type, AppTag.OpenSource(context.getString(R.string.open_source)) ) ) categories?.let { categoryList.addAll(getFusedCategoryBasedOnCategoryType(it, type, tag!!)) } } return Triple(result.getResultStatus(), categoryList, ApplicationApi.APP_TYPE_OPEN) return Pair(categoryList, result.getResultStatus()) } private fun getFusedCategoryBasedOnCategoryType( Loading @@ -180,36 +186,15 @@ class CategoryApiImpl @Inject constructor( ): List<Category> { return when (categoryType) { CategoryType.APPLICATION -> { getCategories(categories, categories.apps, tag) CategoryUtils.getCategories(categories, categories.apps, tag) } CategoryType.GAMES -> { getCategories(categories, categories.games, tag) CategoryUtils.getCategories(categories, categories.games, tag) } } } private fun getCategories( categories: Categories, categoryNames: List<String>, tag: AppTag ) = categoryNames.map { category -> Category( id = category, title = getCategoryTitle(category, categories), drawable = CategoryUtils.provideAppsCategoryIconResource(category), tag = tag ) } private fun getCategoryTitle(category: String, categories: Categories): String { return if (category.contentEquals(CATEGORY_OPEN_GAMES_ID)) { CATEGORY_OPEN_GAMES_TITLE } else { categories.translations.getOrDefault(category, "") } } override suspend fun getGplayAppsByCategory( authData: AuthData, category: String, Loading @@ -228,14 +213,14 @@ class CategoryApiImpl @Inject constructor( } nextPageUrl = streamCluster.clusterNextPageUrl if (!nextPageUrl.isNullOrEmpty()) { if (nextPageUrl.isNotEmpty()) { applicationList.add(Application(isPlaceHolder = true)) } Pair(applicationList, nextPageUrl) } } /** /* * Filter out apps which are restricted, whose details cannot be fetched. * If an app is restricted, we do try to fetch the app details inside a * try-catch block. If that fails, we remove the app, else we keep it even Loading @@ -253,7 +238,11 @@ class CategoryApiImpl @Inject constructor( val filteredApplications = mutableListOf<Application>() return handleNetworkResult { appList.forEach { val filter = applicationDataManager.getAppFilterLevel(it.transformToApplication(context), authData) val filter = applicationDataManager.getAppFilterLevel( it.transformToApplication(context), authData ) if (filter.isUnFiltered()) { filteredApplications.add( it.transformToApplication(context).apply { Loading @@ -266,10 +255,14 @@ class CategoryApiImpl @Inject constructor( } } override suspend fun getPWAApps(category: String): ResultSupreme<Pair<List<Application>, String>> { override suspend fun getCleanApkAppsByCategory( category: String, source: Source ): ResultSupreme<Pair<List<Application>, String>> { val list = mutableListOf<Application>() val result = handleNetworkResult { val response = cleanApkPWARepository.getAppsByCategory(category).body() val response = getCleanApkAppsResponse(source, category) response?.apps?.forEach { applicationDataManager.updateStatus(it) it.updateType() Loading @@ -280,17 +273,18 @@ class CategoryApiImpl @Inject constructor( return ResultSupreme.create(result.getResultStatus(), Pair(list, "")) } override suspend fun getOpenSourceApps(category: String): ResultSupreme<Pair<List<Application>, String>> { val list = mutableListOf<Application>() val result = handleNetworkResult { val response = cleanApkAppsRepository.getAppsByCategory(category).body() response?.apps?.forEach { applicationDataManager.updateStatus(it) it.updateType() applicationDataManager.updateFilterLevel(null, it) list.add(it) private suspend fun getCleanApkAppsResponse( source: Source, category: String ) = when (source) { Source.OPEN -> { cleanApkAppsRepository.getAppsByCategory(category).body() } Source.PWA -> { cleanApkPWARepository.getAppsByCategory(category).body() } return ResultSupreme.create(result.getResultStatus(), Pair(list, "")) else -> null } }
app/src/main/java/foundation/e/apps/data/application/utils/CategoryUtils.kt +44 −0 Original line number Diff line number Diff line Loading @@ -19,9 +19,17 @@ package foundation.e.apps.data.application.utils import foundation.e.apps.R import foundation.e.apps.data.application.data.Category import foundation.e.apps.data.cleanapk.data.categories.Categories import foundation.e.apps.data.enums.AppTag object CategoryUtils { private const val CATEGORY_OPEN_GAMES_ID = "game_open_games" private const val CATEGORY_OPEN_GAMES_TITLE = "Open games" private const val CATEGORY_TITLE_REPLACEABLE_CONJUNCTION = "&" private const val CATEGORY_TITLE_CONJUNCTION = "and" private val categoryIconMap = mapOf( "comics" to R.drawable.ic_cat_comics, "connectivity" to R.drawable.ic_cat_connectivity, Loading Loading @@ -102,6 +110,42 @@ object CategoryUtils { fun provideAppsCategoryIconResource(categoryId: String) = categoryIconMap[categoryId] ?: R.drawable.ic_cat_default fun getCategories( categories: Categories, categoryNames: List<String>, tag: AppTag ) = categoryNames.map { category -> Category( id = category, title = getCategoryTitle(category, categories), drawable = provideAppsCategoryIconResource(category), tag = tag ) } private fun getCategoryTitle(category: String, categories: Categories): String { return if (category.contentEquals(CATEGORY_OPEN_GAMES_ID)) { CATEGORY_OPEN_GAMES_TITLE } else { categories.translations.getOrDefault(category, "") } } fun getCategoryIconName(category: Category): String { var categoryTitle = if (category.tag.getOperationalTag().contentEquals(AppTag.GPlay().getOperationalTag())) category.id else category.title if (categoryTitle.contains(CATEGORY_TITLE_REPLACEABLE_CONJUNCTION)) { categoryTitle = categoryTitle.replace( CATEGORY_TITLE_REPLACEABLE_CONJUNCTION, CATEGORY_TITLE_CONJUNCTION ) } categoryTitle = categoryTitle.replace(' ', '_') return categoryTitle.lowercase() } } enum class CategoryType { Loading
app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt +4 −4 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ class CategoriesViewModel @Inject constructor( private val applicationRepository: ApplicationRepository ) : LoadingViewModel() { val categoriesList: MutableLiveData<Triple<List<Category>, String, ResultStatus>> = val categoriesList: MutableLiveData<Pair<List<Category>, ResultStatus>> = MutableLiveData() fun loadData( Loading @@ -65,17 +65,17 @@ class CategoriesViewModel @Inject constructor( val categoriesData = applicationRepository.getCategoriesList(type) categoriesList.postValue(categoriesData) val status = categoriesData.third val status = categoriesData.second if (status != ResultStatus.OK) { val exception = if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) GPlayException( categoriesData.third == ResultStatus.TIMEOUT, categoriesData.second == ResultStatus.TIMEOUT, status.message.ifBlank { "Data load error" } ) else CleanApkException( categoriesData.third == ResultStatus.TIMEOUT, categoriesData.second == ResultStatus.TIMEOUT, status.message.ifBlank { "Data load error" } ) Loading