Loading app/src/main/java/foundation/e/apps/data/ResultSupreme.kt +16 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ package foundation.e.apps.data import foundation.e.apps.data.enums.ResultStatus import java.util.concurrent.TimeoutException private const val UNKNOWN_ERROR = "Unknown error!" /** * Another implementation of Result class. * This removes the use of [ResultStatus] class for different status. Loading Loading @@ -52,10 +54,12 @@ sealed class ResultSupreme<T> { * Example can be an empty list. * @param exception Optional exception from try-catch block. */ class Timeout<T>(data: T, exception: Exception = TimeoutException()) : class Timeout<T>(data: T? = null, exception: Exception = TimeoutException()) : ResultSupreme<T>() { init { setData(data) data?.let { setData(it) } this.exception = exception } } Loading Loading @@ -119,6 +123,16 @@ sealed class ResultSupreme<T> { this.data = data } fun getResultStatus(): ResultStatus { return when(this) { is Success -> ResultStatus.OK is Timeout -> ResultStatus.TIMEOUT else -> ResultStatus.UNKNOWN.apply { message = this@ResultSupreme.exception?.localizedMessage?: UNKNOWN_ERROR } } } companion object { /** Loading app/src/main/java/foundation/e/apps/data/cleanapk/RetrofitModule.kt +5 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import foundation.e.apps.data.ecloud.EcloudApiInterface import foundation.e.apps.data.exodus.ExodusTrackerApi import foundation.e.apps.data.fdroid.FdroidApiInterface import foundation.e.apps.data.fdroid.FdroidWebInterface import foundation.e.apps.data.gplay.utils.GPlayHttpClient import okhttp3.Cache import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaTypeOrNull Loading @@ -48,6 +49,7 @@ import retrofit2.converter.moshi.MoshiConverterFactory import timber.log.Timber import java.net.ConnectException import java.util.Locale import java.util.concurrent.TimeUnit import javax.inject.Named import javax.inject.Singleton Loading @@ -55,6 +57,8 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) object RetrofitModule { private const val HTTP_TIMEOUT_IN_SECOND = 10L /** * Provides an instance of Retrofit to work with CleanAPK API * @return instance of [CleanApkRetrofit] Loading Loading @@ -208,6 +212,7 @@ object RetrofitModule { fun provideOkHttpClient(cache: Cache, interceptor: Interceptor): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(interceptor) .callTimeout(HTTP_TIMEOUT_IN_SECOND, TimeUnit.SECONDS) .cache(cache) .build() } Loading app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +48 −28 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import android.content.Context import android.text.format.Formatter import androidx.lifecycle.LiveData import androidx.lifecycle.liveData import androidx.lifecycle.map import com.aurora.gplayapi.Constants import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App Loading Loading @@ -73,7 +72,6 @@ import kotlinx.coroutines.Deferred import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.map import kotlinx.coroutines.withTimeout import retrofit2.Response import timber.log.Timber Loading @@ -100,7 +98,7 @@ 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_SEARCH = "Gplay search has failed!" private const val ERROR_GPLAY_API = "Gplay api has faced error!" private const val ERROR_GPLAY_SOURCE_NOT_SELECTED = "Gplay apps are not selected!" } Loading Loading @@ -167,29 +165,32 @@ class FusedApiImpl @Inject constructor( authData: AuthData, ): ResultSupreme<List<FusedHome>> { val apiStatus = when (source) { Source.GPLAY -> runCodeWithTimeout({ val result = when (source) { Source.GPLAY -> handleResultFromAppSources<List<FusedHome>> { priorList.addAll(fetchGPlayHome(authData)) }) priorList } Source.OPEN -> runCodeWithTimeout({ Source.OPEN -> handleResultFromAppSources { val response = (cleanApkAppsRepository.getHomeScreenData() as Response<HomeScreen>).body() response?.home?.let { priorList.addAll(generateCleanAPKHome(it, APP_TYPE_OPEN)) } }) priorList } Source.PWA -> runCodeWithTimeout({ Source.PWA -> handleResultFromAppSources { val response = (cleanApkPWARepository.getHomeScreenData() as Response<HomeScreen>).body() response?.home?.let { priorList.addAll(generateCleanAPKHome(it, APP_TYPE_PWA)) } }) priorList } } setHomeErrorMessage(apiStatus, source) setHomeErrorMessage(result.getResultStatus(), source) priorList.sortByDescending { when (it.source) { APP_TYPE_OPEN -> 2 Loading @@ -197,7 +198,7 @@ class FusedApiImpl @Inject constructor( else -> 3 } } return ResultSupreme.create(apiStatus, priorList) return ResultSupreme.create(result.getResultStatus(), priorList) } private fun setHomeErrorMessage(apiStatus: ResultStatus, source: Source) { Loading Loading @@ -323,16 +324,17 @@ class FusedApiImpl @Inject constructor( searchResult: MutableList<FusedApp>, packageSpecificResults: ArrayList<FusedApp> ): ResultSupreme<Pair<List<FusedApp>, Boolean>> { val status = runCodeWithTimeout({ val result = handleResultFromAppSources { cleanApkResults.addAll(getCleanAPKSearchResults(query)) }) cleanApkResults } if (cleanApkResults.isNotEmpty()) { searchResult.addAll(cleanApkResults) } return ResultSupreme.create( status, result.getResultStatus(), Pair( filterWithKeywordSearch( searchResult, Loading Loading @@ -1083,12 +1085,12 @@ class FusedApiImpl @Inject constructor( query: String, nextPageSubBundle: Set<SearchBundle.SubBundle>? ): GplaySearchResult { try { return handleResultFromAppSources { val searchResults = gplayRepository.getSearchResult(query, nextPageSubBundle?.toMutableSet()) if (!preferenceManagerModule.isGplaySelected()) { return ResultSupreme.Error(ERROR_GPLAY_SOURCE_NOT_SELECTED) return@handleResultFromAppSources Pair(listOf<FusedApp>(), setOf<SearchBundle.SubBundle>()) } val fusedAppList = Loading @@ -1098,21 +1100,39 @@ class FusedApiImpl @Inject constructor( fusedAppList.add(FusedApp(isPlaceHolder = true)) } return ResultSupreme.Success(Pair(fusedAppList.toList(), searchResults.second.toSet())) } catch (e: GplayHttpRequestException) { val message = ( e.localizedMessage?.ifBlank { ERROR_GPLAY_SEARCH } ?: ERROR_GPLAY_SEARCH ) + "Status: ${e.status}" return@handleResultFromAppSources Pair(fusedAppList.toList(), searchResults.second.toSet()) } } private suspend fun <T> handleResultFromAppSources(call: suspend () -> T): ResultSupreme<T> { return try { ResultSupreme.Success(call()) } catch (e: SocketTimeoutException) { val message = extractErrorMessage(e) val exception = GPlayException(true, message) val resultTimeout = ResultSupreme.Timeout<T>(exception = exception) resultTimeout.message = message resultTimeout } catch (e: GplayHttpRequestException) { val message = extractErrorMessage(e) val exception = GPlayException(e.status == 408, message) return ResultSupreme.Error(message, exception) ResultSupreme.Error(message, exception) } catch (e: Exception) { val exception = GPlayException(e is SocketTimeoutException, e.localizedMessage) val message = extractErrorMessage(e) ResultSupreme.Error(message, e) } } return ResultSupreme.Error(e.localizedMessage ?: "", exception) 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" } /* Loading app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt +2 −24 Original line number Diff line number Diff line Loading @@ -54,7 +54,6 @@ class GPlayHttpClient @Inject constructor( companion object { private const val TAG = "GPlayHttpClient" private const val HTTP_TIMEOUT_IN_SECOND = 10L private const val SEARCH = "search" } private val okHttpClient = OkHttpClient().newBuilder() Loading Loading @@ -159,26 +158,8 @@ class GPlayHttpClient @Inject constructor( val call = okHttpClient.newCall(request) buildPlayResponse(call.execute()) } catch (e: Exception) { // TODO: exception will be thrown for all apis when all gplay api implementation // will handle the exceptions. this will be done in following issue. // Issue: https://gitlab.e.foundation/e/os/backlog/-/issues/1483 if (request.url.toString().contains(SEARCH)) { throw e } when (e) { is UnknownHostException, is SocketTimeoutException -> handleExceptionOnGooglePlayRequest(e) else -> handleExceptionOnGooglePlayRequest(e) } } } private fun handleExceptionOnGooglePlayRequest(e: Exception): PlayResponse { Timber.e("processRequest: ${e.localizedMessage}") return PlayResponse().apply { errorString = "${this@GPlayHttpClient::class.java.simpleName}: ${e.localizedMessage}" } } private fun buildUrl(url: String, params: Map<String, String>): HttpUrl { Loading @@ -203,10 +184,7 @@ class GPlayHttpClient @Inject constructor( } } // TODO: exception will be thrown for all apis when all gplay api implementation // will handle the exceptions. this will be done in following issue. // Issue: https://gitlab.e.foundation/e/os/backlog/-/issues/1483 if (response.request.url.toString().contains(SEARCH) && code != 200) { if (code != 200) { throw GplayHttpRequestException(code, response.message) } Loading app/src/main/java/foundation/e/apps/data/login/api/LoginApiRepository.kt +1 −4 Original line number Diff line number Diff line Loading @@ -144,7 +144,6 @@ class LoginApiRepository constructor( */ private suspend fun <T> runCodeWithTimeout( block: suspend () -> T, timeoutBlock: (() -> T?)? = null, exceptionBlock: ((e: Exception) -> T?)? = null, ): ResultSupreme<T?> { return try { Loading @@ -152,9 +151,7 @@ class LoginApiRepository constructor( return@withTimeout ResultSupreme.Success(block()) } } catch (e: TimeoutCancellationException) { ResultSupreme.Timeout(timeoutBlock?.invoke()).apply { message = e.message ?: "" } ResultSupreme.Timeout(exception = e) } catch (e: Exception) { e.printStackTrace() ResultSupreme.Error(exceptionBlock?.invoke(e), message = e.message ?: "") Loading Loading
app/src/main/java/foundation/e/apps/data/ResultSupreme.kt +16 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ package foundation.e.apps.data import foundation.e.apps.data.enums.ResultStatus import java.util.concurrent.TimeoutException private const val UNKNOWN_ERROR = "Unknown error!" /** * Another implementation of Result class. * This removes the use of [ResultStatus] class for different status. Loading Loading @@ -52,10 +54,12 @@ sealed class ResultSupreme<T> { * Example can be an empty list. * @param exception Optional exception from try-catch block. */ class Timeout<T>(data: T, exception: Exception = TimeoutException()) : class Timeout<T>(data: T? = null, exception: Exception = TimeoutException()) : ResultSupreme<T>() { init { setData(data) data?.let { setData(it) } this.exception = exception } } Loading Loading @@ -119,6 +123,16 @@ sealed class ResultSupreme<T> { this.data = data } fun getResultStatus(): ResultStatus { return when(this) { is Success -> ResultStatus.OK is Timeout -> ResultStatus.TIMEOUT else -> ResultStatus.UNKNOWN.apply { message = this@ResultSupreme.exception?.localizedMessage?: UNKNOWN_ERROR } } } companion object { /** Loading
app/src/main/java/foundation/e/apps/data/cleanapk/RetrofitModule.kt +5 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import foundation.e.apps.data.ecloud.EcloudApiInterface import foundation.e.apps.data.exodus.ExodusTrackerApi import foundation.e.apps.data.fdroid.FdroidApiInterface import foundation.e.apps.data.fdroid.FdroidWebInterface import foundation.e.apps.data.gplay.utils.GPlayHttpClient import okhttp3.Cache import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaTypeOrNull Loading @@ -48,6 +49,7 @@ import retrofit2.converter.moshi.MoshiConverterFactory import timber.log.Timber import java.net.ConnectException import java.util.Locale import java.util.concurrent.TimeUnit import javax.inject.Named import javax.inject.Singleton Loading @@ -55,6 +57,8 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) object RetrofitModule { private const val HTTP_TIMEOUT_IN_SECOND = 10L /** * Provides an instance of Retrofit to work with CleanAPK API * @return instance of [CleanApkRetrofit] Loading Loading @@ -208,6 +212,7 @@ object RetrofitModule { fun provideOkHttpClient(cache: Cache, interceptor: Interceptor): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(interceptor) .callTimeout(HTTP_TIMEOUT_IN_SECOND, TimeUnit.SECONDS) .cache(cache) .build() } Loading
app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +48 −28 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import android.content.Context import android.text.format.Formatter import androidx.lifecycle.LiveData import androidx.lifecycle.liveData import androidx.lifecycle.map import com.aurora.gplayapi.Constants import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App Loading Loading @@ -73,7 +72,6 @@ import kotlinx.coroutines.Deferred import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.map import kotlinx.coroutines.withTimeout import retrofit2.Response import timber.log.Timber Loading @@ -100,7 +98,7 @@ 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_SEARCH = "Gplay search has failed!" private const val ERROR_GPLAY_API = "Gplay api has faced error!" private const val ERROR_GPLAY_SOURCE_NOT_SELECTED = "Gplay apps are not selected!" } Loading Loading @@ -167,29 +165,32 @@ class FusedApiImpl @Inject constructor( authData: AuthData, ): ResultSupreme<List<FusedHome>> { val apiStatus = when (source) { Source.GPLAY -> runCodeWithTimeout({ val result = when (source) { Source.GPLAY -> handleResultFromAppSources<List<FusedHome>> { priorList.addAll(fetchGPlayHome(authData)) }) priorList } Source.OPEN -> runCodeWithTimeout({ Source.OPEN -> handleResultFromAppSources { val response = (cleanApkAppsRepository.getHomeScreenData() as Response<HomeScreen>).body() response?.home?.let { priorList.addAll(generateCleanAPKHome(it, APP_TYPE_OPEN)) } }) priorList } Source.PWA -> runCodeWithTimeout({ Source.PWA -> handleResultFromAppSources { val response = (cleanApkPWARepository.getHomeScreenData() as Response<HomeScreen>).body() response?.home?.let { priorList.addAll(generateCleanAPKHome(it, APP_TYPE_PWA)) } }) priorList } } setHomeErrorMessage(apiStatus, source) setHomeErrorMessage(result.getResultStatus(), source) priorList.sortByDescending { when (it.source) { APP_TYPE_OPEN -> 2 Loading @@ -197,7 +198,7 @@ class FusedApiImpl @Inject constructor( else -> 3 } } return ResultSupreme.create(apiStatus, priorList) return ResultSupreme.create(result.getResultStatus(), priorList) } private fun setHomeErrorMessage(apiStatus: ResultStatus, source: Source) { Loading Loading @@ -323,16 +324,17 @@ class FusedApiImpl @Inject constructor( searchResult: MutableList<FusedApp>, packageSpecificResults: ArrayList<FusedApp> ): ResultSupreme<Pair<List<FusedApp>, Boolean>> { val status = runCodeWithTimeout({ val result = handleResultFromAppSources { cleanApkResults.addAll(getCleanAPKSearchResults(query)) }) cleanApkResults } if (cleanApkResults.isNotEmpty()) { searchResult.addAll(cleanApkResults) } return ResultSupreme.create( status, result.getResultStatus(), Pair( filterWithKeywordSearch( searchResult, Loading Loading @@ -1083,12 +1085,12 @@ class FusedApiImpl @Inject constructor( query: String, nextPageSubBundle: Set<SearchBundle.SubBundle>? ): GplaySearchResult { try { return handleResultFromAppSources { val searchResults = gplayRepository.getSearchResult(query, nextPageSubBundle?.toMutableSet()) if (!preferenceManagerModule.isGplaySelected()) { return ResultSupreme.Error(ERROR_GPLAY_SOURCE_NOT_SELECTED) return@handleResultFromAppSources Pair(listOf<FusedApp>(), setOf<SearchBundle.SubBundle>()) } val fusedAppList = Loading @@ -1098,21 +1100,39 @@ class FusedApiImpl @Inject constructor( fusedAppList.add(FusedApp(isPlaceHolder = true)) } return ResultSupreme.Success(Pair(fusedAppList.toList(), searchResults.second.toSet())) } catch (e: GplayHttpRequestException) { val message = ( e.localizedMessage?.ifBlank { ERROR_GPLAY_SEARCH } ?: ERROR_GPLAY_SEARCH ) + "Status: ${e.status}" return@handleResultFromAppSources Pair(fusedAppList.toList(), searchResults.second.toSet()) } } private suspend fun <T> handleResultFromAppSources(call: suspend () -> T): ResultSupreme<T> { return try { ResultSupreme.Success(call()) } catch (e: SocketTimeoutException) { val message = extractErrorMessage(e) val exception = GPlayException(true, message) val resultTimeout = ResultSupreme.Timeout<T>(exception = exception) resultTimeout.message = message resultTimeout } catch (e: GplayHttpRequestException) { val message = extractErrorMessage(e) val exception = GPlayException(e.status == 408, message) return ResultSupreme.Error(message, exception) ResultSupreme.Error(message, exception) } catch (e: Exception) { val exception = GPlayException(e is SocketTimeoutException, e.localizedMessage) val message = extractErrorMessage(e) ResultSupreme.Error(message, e) } } return ResultSupreme.Error(e.localizedMessage ?: "", exception) 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" } /* Loading
app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt +2 −24 Original line number Diff line number Diff line Loading @@ -54,7 +54,6 @@ class GPlayHttpClient @Inject constructor( companion object { private const val TAG = "GPlayHttpClient" private const val HTTP_TIMEOUT_IN_SECOND = 10L private const val SEARCH = "search" } private val okHttpClient = OkHttpClient().newBuilder() Loading Loading @@ -159,26 +158,8 @@ class GPlayHttpClient @Inject constructor( val call = okHttpClient.newCall(request) buildPlayResponse(call.execute()) } catch (e: Exception) { // TODO: exception will be thrown for all apis when all gplay api implementation // will handle the exceptions. this will be done in following issue. // Issue: https://gitlab.e.foundation/e/os/backlog/-/issues/1483 if (request.url.toString().contains(SEARCH)) { throw e } when (e) { is UnknownHostException, is SocketTimeoutException -> handleExceptionOnGooglePlayRequest(e) else -> handleExceptionOnGooglePlayRequest(e) } } } private fun handleExceptionOnGooglePlayRequest(e: Exception): PlayResponse { Timber.e("processRequest: ${e.localizedMessage}") return PlayResponse().apply { errorString = "${this@GPlayHttpClient::class.java.simpleName}: ${e.localizedMessage}" } } private fun buildUrl(url: String, params: Map<String, String>): HttpUrl { Loading @@ -203,10 +184,7 @@ class GPlayHttpClient @Inject constructor( } } // TODO: exception will be thrown for all apis when all gplay api implementation // will handle the exceptions. this will be done in following issue. // Issue: https://gitlab.e.foundation/e/os/backlog/-/issues/1483 if (response.request.url.toString().contains(SEARCH) && code != 200) { if (code != 200) { throw GplayHttpRequestException(code, response.message) } Loading
app/src/main/java/foundation/e/apps/data/login/api/LoginApiRepository.kt +1 −4 Original line number Diff line number Diff line Loading @@ -144,7 +144,6 @@ class LoginApiRepository constructor( */ private suspend fun <T> runCodeWithTimeout( block: suspend () -> T, timeoutBlock: (() -> T?)? = null, exceptionBlock: ((e: Exception) -> T?)? = null, ): ResultSupreme<T?> { return try { Loading @@ -152,9 +151,7 @@ class LoginApiRepository constructor( return@withTimeout ResultSupreme.Success(block()) } } catch (e: TimeoutCancellationException) { ResultSupreme.Timeout(timeoutBlock?.invoke()).apply { message = e.message ?: "" } ResultSupreme.Timeout(exception = e) } catch (e: Exception) { e.printStackTrace() ResultSupreme.Error(exceptionBlock?.invoke(e), message = e.message ?: "") Loading