Loading app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +11 −24 Original line number Original line Diff line number Diff line Loading @@ -48,7 +48,6 @@ import foundation.e.apps.api.fused.data.FusedHome import foundation.e.apps.api.fused.data.Ratings import foundation.e.apps.api.fused.data.Ratings import foundation.e.apps.api.fused.utils.CategoryUtils import foundation.e.apps.api.fused.utils.CategoryUtils import foundation.e.apps.api.gplay.GPlayAPIRepository import foundation.e.apps.api.gplay.GPlayAPIRepository import foundation.e.apps.api.gplay.utils.GPlayHttpClient import foundation.e.apps.home.model.HomeChildFusedAppDiffUtil import foundation.e.apps.home.model.HomeChildFusedAppDiffUtil import foundation.e.apps.login.AuthObject import foundation.e.apps.login.AuthObject import foundation.e.apps.login.LoginSourceRepository import foundation.e.apps.login.LoginSourceRepository Loading Loading @@ -884,7 +883,7 @@ class FusedAPIImpl @Inject constructor( it.updateFilterLevel(authData) it.updateFilterLevel(authData) } } }) }) Timber.d("getAppDetails: $status") return Pair(response ?: FusedApp(), status) return Pair(response ?: FusedApp(), status) } } Loading Loading @@ -1085,34 +1084,20 @@ class FusedAPIImpl @Inject constructor( } } } } private suspend fun FusedAPIImpl.executeBlockHandlingException( private suspend fun executeBlockHandlingException( block: suspend (authData: AuthData?) -> Unit, block: suspend (authData: AuthData?) -> Unit, ): AuthObject? { ): AuthObject? { return try { return try { block(null) block(null) handleUnauthorizedAuthData(block) gPlayAPIRepository.handleUnauthorizedAuthData(block) { loginSourceRepository.validateAuthObject() } } catch (e: ApiException.AppNotFound) { } catch (e: ApiException.AppNotFound) { Timber.w(e) Timber.w(e) handleUnauthorizedAuthData(block) ?: throw e gPlayAPIRepository.handleUnauthorizedAuthData(block) { loginSourceRepository.validateAuthObject() } } ?: throw e } } catch (e: Exception) { throw e private suspend fun handleUnauthorizedAuthData( block: suspend (authData: AuthData?) -> Unit ): AuthObject? { if (!GPlayHttpClient.IS_AUTH_VALID) { val loginSources = loginSourceRepository.getAuthObjects() loginSources.find { it is AuthObject.GPlayAuth }?.let { val authData = (it.result.data as AuthData) GPlayHttpClient.IS_AUTH_VALID = true block.invoke(authData) Timber.d("data refreshed with new auth!") return it } } } } return null } private fun updateCategoryDrawable( private fun updateCategoryDrawable( category: FusedCategory, category: FusedCategory, Loading Loading @@ -1262,7 +1247,9 @@ class FusedAPIImpl @Inject constructor( authData: AuthData authData: AuthData ): LiveData<Pair<List<FusedApp>, Boolean>> { ): LiveData<Pair<List<FusedApp>, Boolean>> { val searchResults = val searchResults = gPlayAPIRepository.getSearchResults(query, authData, ::replaceWithFDroid) gPlayAPIRepository.getSearchResults(query, authData, ::replaceWithFDroid) { loginSourceRepository.validateAuthObject() } return searchResults.map { return searchResults.map { Pair( Pair( it.first, it.first, Loading app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt +24 −4 Original line number Original line Diff line number Diff line Loading @@ -38,12 +38,16 @@ import com.aurora.gplayapi.helpers.StreamHelper import com.aurora.gplayapi.helpers.TopChartsHelper import com.aurora.gplayapi.helpers.TopChartsHelper import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.gplay.utils.GPlayHttpClient import foundation.e.apps.api.gplay.utils.GPlayHttpClient import foundation.e.apps.api.gplay.utils.GplayUtils import foundation.e.apps.login.AuthObject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Inject class GPlayAPIImpl @Inject constructor(private val gPlayHttpClient: GPlayHttpClient) { class GPlayAPIImpl @Inject constructor( private val gPlayHttpClient: GPlayHttpClient ) { suspend fun getSearchSuggestions(query: String, authData: AuthData): List<SearchSuggestEntry> { suspend fun getSearchSuggestions(query: String, authData: AuthData): List<SearchSuggestEntry> { val searchData = mutableListOf<SearchSuggestEntry>() val searchData = mutableListOf<SearchSuggestEntry>() Loading @@ -62,6 +66,7 @@ class GPlayAPIImpl @Inject constructor(private val gPlayHttpClient: GPlayHttpCli query: String, query: String, authData: AuthData, authData: AuthData, replaceWithFDroid: suspend (App) -> FusedApp, replaceWithFDroid: suspend (App) -> FusedApp, authValidator: suspend () -> AuthObject? ): LiveData<Pair<List<FusedApp>, Boolean>> { ): LiveData<Pair<List<FusedApp>, Boolean>> { /* /* * Send livedata to improve UI performance, so we don't have to wait for loading all results. * Send livedata to improve UI performance, so we don't have to wait for loading all results. Loading @@ -73,8 +78,15 @@ class GPlayAPIImpl @Inject constructor(private val gPlayHttpClient: GPlayHttpCli * Variable names and logic made same as that of Aurora store. * Variable names and logic made same as that of Aurora store. * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5171 * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5171 */ */ val searchHelper = SearchHelper(authData).using(gPlayHttpClient) var searchHelper = SearchHelper(authData).using(gPlayHttpClient) val searchBundle = searchHelper.searchResults(query) var searchBundle = searchHelper.searchResults(query) GplayUtils.handleUnauthorizedAuthData({ authData -> authData?.let { searchHelper = SearchHelper(it).using(gPlayHttpClient) searchBundle = searchHelper.searchResults(query) } }, authValidator) val initialReplacedList = mutableListOf<FusedApp>() val initialReplacedList = mutableListOf<FusedApp>() val INITIAL_LIMIT = 4 val INITIAL_LIMIT = 4 Loading @@ -91,7 +103,15 @@ class GPlayAPIImpl @Inject constructor(private val gPlayHttpClient: GPlayHttpCli var nextSubBundleSet: MutableSet<SearchBundle.SubBundle> var nextSubBundleSet: MutableSet<SearchBundle.SubBundle> do { do { nextSubBundleSet = searchBundle.subBundles nextSubBundleSet = searchBundle.subBundles val newSearchBundle = searchHelper.next(nextSubBundleSet) var newSearchBundle = searchHelper.next(nextSubBundleSet) GplayUtils.handleUnauthorizedAuthData({ authData -> authData?.let { searchHelper = SearchHelper(it).using(gPlayHttpClient) newSearchBundle = searchHelper.next(nextSubBundleSet) } }, authValidator) if (newSearchBundle.appList.isNotEmpty()) { if (newSearchBundle.appList.isNotEmpty()) { searchBundle.apply { searchBundle.apply { subBundles.clear() subBundles.clear() Loading app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt +11 −1 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,8 @@ import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.helpers.TopChartsHelper import com.aurora.gplayapi.helpers.TopChartsHelper import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.gplay.utils.GplayUtils import foundation.e.apps.login.AuthObject import javax.inject.Inject import javax.inject.Inject class GPlayAPIRepository @Inject constructor(private val gPlayAPIImpl: GPlayAPIImpl) { class GPlayAPIRepository @Inject constructor(private val gPlayAPIImpl: GPlayAPIImpl) { Loading @@ -40,8 +42,9 @@ class GPlayAPIRepository @Inject constructor(private val gPlayAPIImpl: GPlayAPII query: String, query: String, authData: AuthData, authData: AuthData, replaceWithFDroid: suspend (App) -> FusedApp, replaceWithFDroid: suspend (App) -> FusedApp, authValidator: suspend () -> AuthObject? ): LiveData<Pair<List<FusedApp>, Boolean>> { ): LiveData<Pair<List<FusedApp>, Boolean>> { return gPlayAPIImpl.getSearchResults(query, authData, replaceWithFDroid) return gPlayAPIImpl.getSearchResults(query, authData, replaceWithFDroid, authValidator) } } suspend fun getOnDemandModule( suspend fun getOnDemandModule( Loading Loading @@ -109,4 +112,11 @@ class GPlayAPIRepository @Inject constructor(private val gPlayAPIImpl: GPlayAPII suspend fun listApps(browseUrl: String, authData: AuthData): List<App> { suspend fun listApps(browseUrl: String, authData: AuthData): List<App> { return gPlayAPIImpl.listApps(browseUrl, authData) return gPlayAPIImpl.listApps(browseUrl, authData) } } suspend fun handleUnauthorizedAuthData( block: suspend (authData: AuthData?) -> Unit, authDataFetcher: suspend () -> AuthObject? ): AuthObject? { return GplayUtils.handleUnauthorizedAuthData(block, authDataFetcher) } } } app/src/main/java/foundation/e/apps/api/gplay/utils/GplayUtils.kt 0 → 100644 +42 −0 Original line number Original line 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.api.gplay.utils import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.login.AuthObject import timber.log.Timber object GplayUtils { suspend fun handleUnauthorizedAuthData( block: suspend (authData: AuthData?) -> Unit, authValidator: suspend () -> AuthObject? ): AuthObject? { if (!GPlayHttpClient.IS_AUTH_VALID) { authValidator()?.let { val authData = (it.result.data as AuthData) GPlayHttpClient.IS_AUTH_VALID = true block.invoke(authData) Timber.d("data refreshed with new auth!") return it } } return null } } No newline at end of file app/src/main/java/foundation/e/apps/login/AuthValidator.kt 0 → 100644 +23 −0 Original line number Original line 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.login interface AuthValidator { suspend fun validateAuth(): AuthObject? } No newline at end of file Loading
app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +11 −24 Original line number Original line Diff line number Diff line Loading @@ -48,7 +48,6 @@ import foundation.e.apps.api.fused.data.FusedHome import foundation.e.apps.api.fused.data.Ratings import foundation.e.apps.api.fused.data.Ratings import foundation.e.apps.api.fused.utils.CategoryUtils import foundation.e.apps.api.fused.utils.CategoryUtils import foundation.e.apps.api.gplay.GPlayAPIRepository import foundation.e.apps.api.gplay.GPlayAPIRepository import foundation.e.apps.api.gplay.utils.GPlayHttpClient import foundation.e.apps.home.model.HomeChildFusedAppDiffUtil import foundation.e.apps.home.model.HomeChildFusedAppDiffUtil import foundation.e.apps.login.AuthObject import foundation.e.apps.login.AuthObject import foundation.e.apps.login.LoginSourceRepository import foundation.e.apps.login.LoginSourceRepository Loading Loading @@ -884,7 +883,7 @@ class FusedAPIImpl @Inject constructor( it.updateFilterLevel(authData) it.updateFilterLevel(authData) } } }) }) Timber.d("getAppDetails: $status") return Pair(response ?: FusedApp(), status) return Pair(response ?: FusedApp(), status) } } Loading Loading @@ -1085,34 +1084,20 @@ class FusedAPIImpl @Inject constructor( } } } } private suspend fun FusedAPIImpl.executeBlockHandlingException( private suspend fun executeBlockHandlingException( block: suspend (authData: AuthData?) -> Unit, block: suspend (authData: AuthData?) -> Unit, ): AuthObject? { ): AuthObject? { return try { return try { block(null) block(null) handleUnauthorizedAuthData(block) gPlayAPIRepository.handleUnauthorizedAuthData(block) { loginSourceRepository.validateAuthObject() } } catch (e: ApiException.AppNotFound) { } catch (e: ApiException.AppNotFound) { Timber.w(e) Timber.w(e) handleUnauthorizedAuthData(block) ?: throw e gPlayAPIRepository.handleUnauthorizedAuthData(block) { loginSourceRepository.validateAuthObject() } } ?: throw e } } catch (e: Exception) { throw e private suspend fun handleUnauthorizedAuthData( block: suspend (authData: AuthData?) -> Unit ): AuthObject? { if (!GPlayHttpClient.IS_AUTH_VALID) { val loginSources = loginSourceRepository.getAuthObjects() loginSources.find { it is AuthObject.GPlayAuth }?.let { val authData = (it.result.data as AuthData) GPlayHttpClient.IS_AUTH_VALID = true block.invoke(authData) Timber.d("data refreshed with new auth!") return it } } } } return null } private fun updateCategoryDrawable( private fun updateCategoryDrawable( category: FusedCategory, category: FusedCategory, Loading Loading @@ -1262,7 +1247,9 @@ class FusedAPIImpl @Inject constructor( authData: AuthData authData: AuthData ): LiveData<Pair<List<FusedApp>, Boolean>> { ): LiveData<Pair<List<FusedApp>, Boolean>> { val searchResults = val searchResults = gPlayAPIRepository.getSearchResults(query, authData, ::replaceWithFDroid) gPlayAPIRepository.getSearchResults(query, authData, ::replaceWithFDroid) { loginSourceRepository.validateAuthObject() } return searchResults.map { return searchResults.map { Pair( Pair( it.first, it.first, Loading
app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIImpl.kt +24 −4 Original line number Original line Diff line number Diff line Loading @@ -38,12 +38,16 @@ import com.aurora.gplayapi.helpers.StreamHelper import com.aurora.gplayapi.helpers.TopChartsHelper import com.aurora.gplayapi.helpers.TopChartsHelper import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.gplay.utils.GPlayHttpClient import foundation.e.apps.api.gplay.utils.GPlayHttpClient import foundation.e.apps.api.gplay.utils.GplayUtils import foundation.e.apps.login.AuthObject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Inject class GPlayAPIImpl @Inject constructor(private val gPlayHttpClient: GPlayHttpClient) { class GPlayAPIImpl @Inject constructor( private val gPlayHttpClient: GPlayHttpClient ) { suspend fun getSearchSuggestions(query: String, authData: AuthData): List<SearchSuggestEntry> { suspend fun getSearchSuggestions(query: String, authData: AuthData): List<SearchSuggestEntry> { val searchData = mutableListOf<SearchSuggestEntry>() val searchData = mutableListOf<SearchSuggestEntry>() Loading @@ -62,6 +66,7 @@ class GPlayAPIImpl @Inject constructor(private val gPlayHttpClient: GPlayHttpCli query: String, query: String, authData: AuthData, authData: AuthData, replaceWithFDroid: suspend (App) -> FusedApp, replaceWithFDroid: suspend (App) -> FusedApp, authValidator: suspend () -> AuthObject? ): LiveData<Pair<List<FusedApp>, Boolean>> { ): LiveData<Pair<List<FusedApp>, Boolean>> { /* /* * Send livedata to improve UI performance, so we don't have to wait for loading all results. * Send livedata to improve UI performance, so we don't have to wait for loading all results. Loading @@ -73,8 +78,15 @@ class GPlayAPIImpl @Inject constructor(private val gPlayHttpClient: GPlayHttpCli * Variable names and logic made same as that of Aurora store. * Variable names and logic made same as that of Aurora store. * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5171 * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5171 */ */ val searchHelper = SearchHelper(authData).using(gPlayHttpClient) var searchHelper = SearchHelper(authData).using(gPlayHttpClient) val searchBundle = searchHelper.searchResults(query) var searchBundle = searchHelper.searchResults(query) GplayUtils.handleUnauthorizedAuthData({ authData -> authData?.let { searchHelper = SearchHelper(it).using(gPlayHttpClient) searchBundle = searchHelper.searchResults(query) } }, authValidator) val initialReplacedList = mutableListOf<FusedApp>() val initialReplacedList = mutableListOf<FusedApp>() val INITIAL_LIMIT = 4 val INITIAL_LIMIT = 4 Loading @@ -91,7 +103,15 @@ class GPlayAPIImpl @Inject constructor(private val gPlayHttpClient: GPlayHttpCli var nextSubBundleSet: MutableSet<SearchBundle.SubBundle> var nextSubBundleSet: MutableSet<SearchBundle.SubBundle> do { do { nextSubBundleSet = searchBundle.subBundles nextSubBundleSet = searchBundle.subBundles val newSearchBundle = searchHelper.next(nextSubBundleSet) var newSearchBundle = searchHelper.next(nextSubBundleSet) GplayUtils.handleUnauthorizedAuthData({ authData -> authData?.let { searchHelper = SearchHelper(it).using(gPlayHttpClient) newSearchBundle = searchHelper.next(nextSubBundleSet) } }, authValidator) if (newSearchBundle.appList.isNotEmpty()) { if (newSearchBundle.appList.isNotEmpty()) { searchBundle.apply { searchBundle.apply { subBundles.clear() subBundles.clear() Loading
app/src/main/java/foundation/e/apps/api/gplay/GPlayAPIRepository.kt +11 −1 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,8 @@ import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.helpers.TopChartsHelper import com.aurora.gplayapi.helpers.TopChartsHelper import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.api.gplay.utils.GplayUtils import foundation.e.apps.login.AuthObject import javax.inject.Inject import javax.inject.Inject class GPlayAPIRepository @Inject constructor(private val gPlayAPIImpl: GPlayAPIImpl) { class GPlayAPIRepository @Inject constructor(private val gPlayAPIImpl: GPlayAPIImpl) { Loading @@ -40,8 +42,9 @@ class GPlayAPIRepository @Inject constructor(private val gPlayAPIImpl: GPlayAPII query: String, query: String, authData: AuthData, authData: AuthData, replaceWithFDroid: suspend (App) -> FusedApp, replaceWithFDroid: suspend (App) -> FusedApp, authValidator: suspend () -> AuthObject? ): LiveData<Pair<List<FusedApp>, Boolean>> { ): LiveData<Pair<List<FusedApp>, Boolean>> { return gPlayAPIImpl.getSearchResults(query, authData, replaceWithFDroid) return gPlayAPIImpl.getSearchResults(query, authData, replaceWithFDroid, authValidator) } } suspend fun getOnDemandModule( suspend fun getOnDemandModule( Loading Loading @@ -109,4 +112,11 @@ class GPlayAPIRepository @Inject constructor(private val gPlayAPIImpl: GPlayAPII suspend fun listApps(browseUrl: String, authData: AuthData): List<App> { suspend fun listApps(browseUrl: String, authData: AuthData): List<App> { return gPlayAPIImpl.listApps(browseUrl, authData) return gPlayAPIImpl.listApps(browseUrl, authData) } } suspend fun handleUnauthorizedAuthData( block: suspend (authData: AuthData?) -> Unit, authDataFetcher: suspend () -> AuthObject? ): AuthObject? { return GplayUtils.handleUnauthorizedAuthData(block, authDataFetcher) } } }
app/src/main/java/foundation/e/apps/api/gplay/utils/GplayUtils.kt 0 → 100644 +42 −0 Original line number Original line 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.api.gplay.utils import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.login.AuthObject import timber.log.Timber object GplayUtils { suspend fun handleUnauthorizedAuthData( block: suspend (authData: AuthData?) -> Unit, authValidator: suspend () -> AuthObject? ): AuthObject? { if (!GPlayHttpClient.IS_AUTH_VALID) { authValidator()?.let { val authData = (it.result.data as AuthData) GPlayHttpClient.IS_AUTH_VALID = true block.invoke(authData) Timber.d("data refreshed with new auth!") return it } } return null } } No newline at end of file
app/src/main/java/foundation/e/apps/login/AuthValidator.kt 0 → 100644 +23 −0 Original line number Original line 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.login interface AuthValidator { suspend fun validateAuth(): AuthObject? } No newline at end of file