Loading app/src/main/java/foundation/e/apps/data/NetworkHandler.kt 0 → 100644 +44 −0 Original line number Diff line number Diff line package foundation.e.apps.data import foundation.e.apps.data.gplay.utils.GPlayHttpClient import foundation.e.apps.data.gplay.utils.GplayHttpRequestException import foundation.e.apps.data.login.exceptions.GPlayException import java.net.SocketTimeoutException private const val TIMEOUT = "Timeout" private const val UNKNOWN = "Unknown" private const val STATUS = "Status:" private const val ERROR_GPLAY_API = "Gplay api has faced error!" suspend fun <T> handleNetworkResult(call: suspend () -> T): ResultSupreme<T> { return try { ResultSupreme.Success(call()) } catch (e: SocketTimeoutException) { val message = extractErrorMessage(e) val resultTimeout = ResultSupreme.Timeout<T>(exception = e) resultTimeout.message = message resultTimeout } catch (e: GplayHttpRequestException) { val message = extractErrorMessage(e) val exception = GPlayException(e.status == GPlayHttpClient.STATUS_CODE_TIMEOUT, message) if (exception.isTimeout) { ResultSupreme.Timeout(exception = exception) } else { ResultSupreme.Error(message, exception) } } catch (e: Exception) { val message = extractErrorMessage(e) ResultSupreme.Error(message, e) } } private fun extractErrorMessage(e: Exception): String { val status = when (e) { is GplayHttpRequestException -> e.status.toString() is SocketTimeoutException -> TIMEOUT else -> UNKNOWN } return (e.localizedMessage?.ifBlank { ERROR_GPLAY_API } ?: ERROR_GPLAY_API) + " $STATUS $status" } No newline at end of file app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +5 −35 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ import foundation.e.apps.data.fused.utils.CategoryUtils import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.gplay.GplayStoreRepository import foundation.e.apps.data.gplay.utils.GplayHttpRequestException import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.data.login.exceptions.GPlayException import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.install.pkg.PWAManagerModule Loading Loading @@ -98,10 +99,6 @@ class FusedApiImpl @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 ERROR_GPLAY_API = "Gplay api has faced error!" private const val TIMEOUT = "Timeout" private const val UNKNOWN = "Unknown" private const val STATUS = "Status: " } /** Loading Loading @@ -383,7 +380,10 @@ class FusedApiImpl @Inject constructor( * Also send true in the pair to signal more results being loaded. */ if (result.getResultStatus() != ResultStatus.OK) { return ResultSupreme.create(result.getResultStatus(), Pair(packageSpecificResults, false)) return ResultSupreme.create( result.getResultStatus(), Pair(packageSpecificResults, false) ) } return ResultSupreme.create(result.getResultStatus(), Pair(packageSpecificResults, true)) } Loading Loading @@ -1086,36 +1086,6 @@ class FusedApiImpl @Inject constructor( } } private suspend fun <T> handleNetworkResult(call: suspend () -> T): ResultSupreme<T> { return try { ResultSupreme.Success(call()) } catch (e: SocketTimeoutException) { val message = extractErrorMessage(e) val resultTimeout = ResultSupreme.Timeout<T>(exception = e) resultTimeout.message = message resultTimeout } catch (e: GplayHttpRequestException) { val message = extractErrorMessage(e) val exception = GPlayException(e.status == 408, message) ResultSupreme.Error(message, exception) } catch (e: Exception) { val message = extractErrorMessage(e) ResultSupreme.Error(message, e) } } private fun extractErrorMessage(e: Exception): String { val status = when (e) { is GplayHttpRequestException -> e.status.toString() is SocketTimeoutException -> TIMEOUT else -> UNKNOWN } return (e.localizedMessage?.ifBlank { ERROR_GPLAY_API } ?: ERROR_GPLAY_API) + "$STATUS$status" } /* * This function will replace a GPlay app with F-Droid app if exists, * else will show the GPlay app itself. Loading app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt +5 −1 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import java.net.UnknownHostException import java.util.concurrent.TimeUnit import javax.inject.Inject class GPlayHttpClient @Inject constructor( private val cache: Cache, ) : IHttpClient { Loading @@ -59,6 +60,7 @@ class GPlayHttpClient @Inject constructor( private const val SEARCH_SUGGEST = "searchSuggest" private const val STATUS_CODE_UNAUTHORIZED = 401 private const val STATUS_CODE_TOO_MANY_REQUESTS = 429 const val STATUS_CODE_TIMEOUT = 408 } private val okHttpClient = OkHttpClient().newBuilder() Loading Loading @@ -164,8 +166,10 @@ class GPlayHttpClient @Inject constructor( val call = okHttpClient.newCall(request) response = call.execute() buildPlayResponse(response) } catch (e: GplayHttpRequestException) { throw e } catch (e: Exception) { val status = if (e is SocketTimeoutException) 408 else -1 val status = if (e is SocketTimeoutException) STATUS_CODE_TIMEOUT else -1 throw GplayHttpRequestException(status, e.localizedMessage ?: "") } finally { response?.close() Loading app/src/main/java/foundation/e/apps/data/login/api/LoginApiRepository.kt +8 −26 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import foundation.e.apps.data.Constants.timeoutDurationInMillis import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.User import foundation.e.apps.data.gplay.utils.AC2DMUtil import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.data.login.exceptions.GPlayLoginException import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.withTimeout Loading Loading @@ -52,9 +53,9 @@ class LoginApiRepository constructor( * else blank for Anonymous login. */ suspend fun fetchAuthData(email: String, aasToken: String, locale: Locale): ResultSupreme<AuthData?> { val result = runCodeWithTimeout({ val result = handleNetworkResult { gPlayLoginInterface.fetchAuthData(email, aasToken) }) } return result.apply { this.data?.locale = locale this.exception = when (result) { Loading @@ -76,13 +77,13 @@ class LoginApiRepository constructor( */ suspend fun login(authData: AuthData): ResultSupreme<PlayResponse> { var response = PlayResponse() val result = runCodeWithTimeout({ val result = handleNetworkResult { response = gPlayLoginInterface.login(authData) if (response.code != 200) { throw Exception("Validation network code: ${response.code}") } response }) } return ResultSupreme.replicate(result, response).apply { this.exception = when (result) { is ResultSupreme.Timeout -> GPlayLoginException(true, "GPlay API timeout", user) Loading @@ -109,8 +110,8 @@ class LoginApiRepository constructor( googleLoginApi: GoogleLoginApi, email: String, oauthToken: String ): ResultSupreme<String?> { val result = runCodeWithTimeout({ ): ResultSupreme<String> { val result = handleNetworkResult { var aasToken = "" val response = googleLoginApi.getAC2DMResponse(email, oauthToken) var error = response.errorString Loading @@ -129,7 +130,7 @@ class LoginApiRepository constructor( throw Exception(error) } aasToken }) } return result.apply { this.exception = when (result) { is ResultSupreme.Timeout -> GPlayLoginException(true, "GPlay API timeout", User.GOOGLE) Loading @@ -138,23 +139,4 @@ class LoginApiRepository constructor( } } } /** * Utility method to run a specified code block in a fixed amount of time. */ private suspend fun <T> runCodeWithTimeout( block: suspend () -> T, exceptionBlock: ((e: Exception) -> T?)? = null, ): ResultSupreme<T?> { return try { withTimeout(timeoutDurationInMillis) { return@withTimeout ResultSupreme.Success(block()) } } catch (e: TimeoutCancellationException) { ResultSupreme.Timeout(exception = e) } catch (e: Exception) { e.printStackTrace() ResultSupreme.Error(exceptionBlock?.invoke(e), message = e.message ?: "") } } } app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt +13 −13 Original line number Diff line number Diff line Loading @@ -42,9 +42,11 @@ class ApplicationListViewModel @Inject constructor( val appListLiveData: MutableLiveData<ResultSupreme<List<FusedApp>>?> = MutableLiveData() var isLoading = false private var isLoading = false var nextPageUrl: String? = null private var nextPageUrl: String? = null private var currentAuthListObject: List<AuthObject>? = null fun loadData( category: String, Loading @@ -54,11 +56,18 @@ class ApplicationListViewModel @Inject constructor( ) { super.onLoadData(authObjectList, { successAuthList, _ -> if (appListLiveData.value?.data?.isNotEmpty() == true) { // if token is refreshed, then reset all data if (currentAuthListObject != null && currentAuthListObject != authObjectList) { appListLiveData.postValue(ResultSupreme.Success(emptyList())) nextPageUrl = null } if (appListLiveData.value?.data?.isNotEmpty() == true && currentAuthListObject == authObjectList) { appListLiveData.postValue(appListLiveData.value) return@onLoadData } this.currentAuthListObject = authObjectList successAuthList.find { it is AuthObject.GPlayAuth }?.run { getList(category, result.data!! as AuthData, source) return@onLoadData Loading Loading @@ -163,18 +172,9 @@ class ApplicationListViewModel @Inject constructor( private fun appendAppList(it: Pair<List<FusedApp>, String>): List<FusedApp>? { val currentAppList = appListLiveData.value?.data?.toMutableList() currentAppList?.removeIf { item -> item.isPlaceHolder } val appList = currentAppList?.plus(it.first) return appList return currentAppList?.plus(it.first) } /** * @return returns true if there is changes in data, otherwise false */ fun isAnyAppUpdated( newFusedApps: List<FusedApp>, oldFusedApps: List<FusedApp> ) = fusedAPIRepository.isAnyFusedAppUpdated(newFusedApps, oldFusedApps) fun hasAnyAppInstallStatusChanged(currentList: List<FusedApp>) = fusedAPIRepository.isAnyAppInstallStatusChanged(currentList) } Loading
app/src/main/java/foundation/e/apps/data/NetworkHandler.kt 0 → 100644 +44 −0 Original line number Diff line number Diff line package foundation.e.apps.data import foundation.e.apps.data.gplay.utils.GPlayHttpClient import foundation.e.apps.data.gplay.utils.GplayHttpRequestException import foundation.e.apps.data.login.exceptions.GPlayException import java.net.SocketTimeoutException private const val TIMEOUT = "Timeout" private const val UNKNOWN = "Unknown" private const val STATUS = "Status:" private const val ERROR_GPLAY_API = "Gplay api has faced error!" suspend fun <T> handleNetworkResult(call: suspend () -> T): ResultSupreme<T> { return try { ResultSupreme.Success(call()) } catch (e: SocketTimeoutException) { val message = extractErrorMessage(e) val resultTimeout = ResultSupreme.Timeout<T>(exception = e) resultTimeout.message = message resultTimeout } catch (e: GplayHttpRequestException) { val message = extractErrorMessage(e) val exception = GPlayException(e.status == GPlayHttpClient.STATUS_CODE_TIMEOUT, message) if (exception.isTimeout) { ResultSupreme.Timeout(exception = exception) } else { ResultSupreme.Error(message, exception) } } catch (e: Exception) { val message = extractErrorMessage(e) ResultSupreme.Error(message, e) } } private fun extractErrorMessage(e: Exception): String { val status = when (e) { is GplayHttpRequestException -> e.status.toString() is SocketTimeoutException -> TIMEOUT else -> UNKNOWN } return (e.localizedMessage?.ifBlank { ERROR_GPLAY_API } ?: ERROR_GPLAY_API) + " $STATUS $status" } No newline at end of file
app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +5 −35 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ import foundation.e.apps.data.fused.utils.CategoryUtils import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.gplay.GplayStoreRepository import foundation.e.apps.data.gplay.utils.GplayHttpRequestException import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.data.login.exceptions.GPlayException import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.install.pkg.PWAManagerModule Loading Loading @@ -98,10 +99,6 @@ class FusedApiImpl @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 ERROR_GPLAY_API = "Gplay api has faced error!" private const val TIMEOUT = "Timeout" private const val UNKNOWN = "Unknown" private const val STATUS = "Status: " } /** Loading Loading @@ -383,7 +380,10 @@ class FusedApiImpl @Inject constructor( * Also send true in the pair to signal more results being loaded. */ if (result.getResultStatus() != ResultStatus.OK) { return ResultSupreme.create(result.getResultStatus(), Pair(packageSpecificResults, false)) return ResultSupreme.create( result.getResultStatus(), Pair(packageSpecificResults, false) ) } return ResultSupreme.create(result.getResultStatus(), Pair(packageSpecificResults, true)) } Loading Loading @@ -1086,36 +1086,6 @@ class FusedApiImpl @Inject constructor( } } private suspend fun <T> handleNetworkResult(call: suspend () -> T): ResultSupreme<T> { return try { ResultSupreme.Success(call()) } catch (e: SocketTimeoutException) { val message = extractErrorMessage(e) val resultTimeout = ResultSupreme.Timeout<T>(exception = e) resultTimeout.message = message resultTimeout } catch (e: GplayHttpRequestException) { val message = extractErrorMessage(e) val exception = GPlayException(e.status == 408, message) ResultSupreme.Error(message, exception) } catch (e: Exception) { val message = extractErrorMessage(e) ResultSupreme.Error(message, e) } } private fun extractErrorMessage(e: Exception): String { val status = when (e) { is GplayHttpRequestException -> e.status.toString() is SocketTimeoutException -> TIMEOUT else -> UNKNOWN } return (e.localizedMessage?.ifBlank { ERROR_GPLAY_API } ?: ERROR_GPLAY_API) + "$STATUS$status" } /* * This function will replace a GPlay app with F-Droid app if exists, * else will show the GPlay app itself. Loading
app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt +5 −1 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ import java.net.UnknownHostException import java.util.concurrent.TimeUnit import javax.inject.Inject class GPlayHttpClient @Inject constructor( private val cache: Cache, ) : IHttpClient { Loading @@ -59,6 +60,7 @@ class GPlayHttpClient @Inject constructor( private const val SEARCH_SUGGEST = "searchSuggest" private const val STATUS_CODE_UNAUTHORIZED = 401 private const val STATUS_CODE_TOO_MANY_REQUESTS = 429 const val STATUS_CODE_TIMEOUT = 408 } private val okHttpClient = OkHttpClient().newBuilder() Loading Loading @@ -164,8 +166,10 @@ class GPlayHttpClient @Inject constructor( val call = okHttpClient.newCall(request) response = call.execute() buildPlayResponse(response) } catch (e: GplayHttpRequestException) { throw e } catch (e: Exception) { val status = if (e is SocketTimeoutException) 408 else -1 val status = if (e is SocketTimeoutException) STATUS_CODE_TIMEOUT else -1 throw GplayHttpRequestException(status, e.localizedMessage ?: "") } finally { response?.close() Loading
app/src/main/java/foundation/e/apps/data/login/api/LoginApiRepository.kt +8 −26 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import foundation.e.apps.data.Constants.timeoutDurationInMillis import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.User import foundation.e.apps.data.gplay.utils.AC2DMUtil import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.data.login.exceptions.GPlayLoginException import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.withTimeout Loading Loading @@ -52,9 +53,9 @@ class LoginApiRepository constructor( * else blank for Anonymous login. */ suspend fun fetchAuthData(email: String, aasToken: String, locale: Locale): ResultSupreme<AuthData?> { val result = runCodeWithTimeout({ val result = handleNetworkResult { gPlayLoginInterface.fetchAuthData(email, aasToken) }) } return result.apply { this.data?.locale = locale this.exception = when (result) { Loading @@ -76,13 +77,13 @@ class LoginApiRepository constructor( */ suspend fun login(authData: AuthData): ResultSupreme<PlayResponse> { var response = PlayResponse() val result = runCodeWithTimeout({ val result = handleNetworkResult { response = gPlayLoginInterface.login(authData) if (response.code != 200) { throw Exception("Validation network code: ${response.code}") } response }) } return ResultSupreme.replicate(result, response).apply { this.exception = when (result) { is ResultSupreme.Timeout -> GPlayLoginException(true, "GPlay API timeout", user) Loading @@ -109,8 +110,8 @@ class LoginApiRepository constructor( googleLoginApi: GoogleLoginApi, email: String, oauthToken: String ): ResultSupreme<String?> { val result = runCodeWithTimeout({ ): ResultSupreme<String> { val result = handleNetworkResult { var aasToken = "" val response = googleLoginApi.getAC2DMResponse(email, oauthToken) var error = response.errorString Loading @@ -129,7 +130,7 @@ class LoginApiRepository constructor( throw Exception(error) } aasToken }) } return result.apply { this.exception = when (result) { is ResultSupreme.Timeout -> GPlayLoginException(true, "GPlay API timeout", User.GOOGLE) Loading @@ -138,23 +139,4 @@ class LoginApiRepository constructor( } } } /** * Utility method to run a specified code block in a fixed amount of time. */ private suspend fun <T> runCodeWithTimeout( block: suspend () -> T, exceptionBlock: ((e: Exception) -> T?)? = null, ): ResultSupreme<T?> { return try { withTimeout(timeoutDurationInMillis) { return@withTimeout ResultSupreme.Success(block()) } } catch (e: TimeoutCancellationException) { ResultSupreme.Timeout(exception = e) } catch (e: Exception) { e.printStackTrace() ResultSupreme.Error(exceptionBlock?.invoke(e), message = e.message ?: "") } } }
app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt +13 −13 Original line number Diff line number Diff line Loading @@ -42,9 +42,11 @@ class ApplicationListViewModel @Inject constructor( val appListLiveData: MutableLiveData<ResultSupreme<List<FusedApp>>?> = MutableLiveData() var isLoading = false private var isLoading = false var nextPageUrl: String? = null private var nextPageUrl: String? = null private var currentAuthListObject: List<AuthObject>? = null fun loadData( category: String, Loading @@ -54,11 +56,18 @@ class ApplicationListViewModel @Inject constructor( ) { super.onLoadData(authObjectList, { successAuthList, _ -> if (appListLiveData.value?.data?.isNotEmpty() == true) { // if token is refreshed, then reset all data if (currentAuthListObject != null && currentAuthListObject != authObjectList) { appListLiveData.postValue(ResultSupreme.Success(emptyList())) nextPageUrl = null } if (appListLiveData.value?.data?.isNotEmpty() == true && currentAuthListObject == authObjectList) { appListLiveData.postValue(appListLiveData.value) return@onLoadData } this.currentAuthListObject = authObjectList successAuthList.find { it is AuthObject.GPlayAuth }?.run { getList(category, result.data!! as AuthData, source) return@onLoadData Loading Loading @@ -163,18 +172,9 @@ class ApplicationListViewModel @Inject constructor( private fun appendAppList(it: Pair<List<FusedApp>, String>): List<FusedApp>? { val currentAppList = appListLiveData.value?.data?.toMutableList() currentAppList?.removeIf { item -> item.isPlaceHolder } val appList = currentAppList?.plus(it.first) return appList return currentAppList?.plus(it.first) } /** * @return returns true if there is changes in data, otherwise false */ fun isAnyAppUpdated( newFusedApps: List<FusedApp>, oldFusedApps: List<FusedApp> ) = fusedAPIRepository.isAnyFusedAppUpdated(newFusedApps, oldFusedApps) fun hasAnyAppInstallStatusChanged(currentList: List<FusedApp>) = fusedAPIRepository.isAnyAppInstallStatusChanged(currentList) }