Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e67e3bd6 authored by Hasib Prince's avatar Hasib Prince
Browse files

Merge branch '1483-fix_timeout' into 'main'

1483 fix timeout

See merge request !357
parents 6c43312d 203974c7
Loading
Loading
Loading
Loading
Loading
+73 −0
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

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) {
        handleSocketTimeoutException(e)
    } catch (e: GplayHttpRequestException) {
        resultSupremeGplayHttpRequestException(e)
    } catch (e: Exception) {
        handleOthersException(e)
    }
}

private fun <T> handleSocketTimeoutException(e: SocketTimeoutException): ResultSupreme.Timeout<T> {
    val message = extractErrorMessage(e)
    val resultTimeout = ResultSupreme.Timeout<T>(exception = e)
    resultTimeout.message = message
    return resultTimeout
}

private fun <T> resultSupremeGplayHttpRequestException(e: GplayHttpRequestException): ResultSupreme<T> {
    val message = extractErrorMessage(e)
    val exception = GPlayException(e.status == GPlayHttpClient.STATUS_CODE_TIMEOUT, message)

    return if (exception.isTimeout) {
        ResultSupreme.Timeout(exception = exception)
    } else {
        ResultSupreme.Error(message, exception)
    }
}

private fun <T> handleOthersException(e: Exception): ResultSupreme.Error<T> {
    val message = extractErrorMessage(e)
    return 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"
}
+16 −2
Original line number Diff line number Diff line
@@ -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.
@@ -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
        }
    }
@@ -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 {

        /**
+4 −0
Original line number Diff line number Diff line
@@ -48,6 +48,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

@@ -55,6 +56,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]
@@ -208,6 +211,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()
    }
+81 −114
Original line number Diff line number Diff line
@@ -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
@@ -63,8 +62,7 @@ import foundation.e.apps.data.fused.utils.CategoryType
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.login.exceptions.GPlayException
import foundation.e.apps.data.handleNetworkResult
import foundation.e.apps.data.preference.PreferenceManagerModule
import foundation.e.apps.install.pkg.PWAManagerModule
import foundation.e.apps.install.pkg.PkgManagerModule
@@ -73,11 +71,9 @@ 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
import java.net.SocketTimeoutException
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
@@ -100,8 +96,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_SEARCH = "Gplay search has failed!"
        private const val ERROR_GPLAY_SOURCE_NOT_SELECTED = "Gplay apps are not selected!"
    }

    /**
@@ -167,29 +161,32 @@ class FusedApiImpl @Inject constructor(
        authData: AuthData,
    ): ResultSupreme<List<FusedHome>> {

        val apiStatus = when (source) {
            Source.GPLAY -> runCodeWithTimeout({
        val result = when (source) {
            Source.GPLAY -> handleNetworkResult<List<FusedHome>> {
                priorList.addAll(fetchGPlayHome(authData))
            })
                priorList
            }

            Source.OPEN -> runCodeWithTimeout({
            Source.OPEN -> handleNetworkResult {
                val response =
                    (cleanApkAppsRepository.getHomeScreenData() as Response<HomeScreen>).body()
                response?.home?.let {
                    priorList.addAll(generateCleanAPKHome(it, APP_TYPE_OPEN))
                }
            })
                priorList
            }

            Source.PWA -> runCodeWithTimeout({
            Source.PWA -> handleNetworkResult {
                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
@@ -197,7 +194,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) {
@@ -289,7 +286,7 @@ class FusedApiImpl @Inject constructor(
        packageSpecificResults: ArrayList<FusedApp>
    ): ResultSupreme<Pair<List<FusedApp>, Boolean>> {
        val pwaApps: MutableList<FusedApp> = mutableListOf()
        val status = runCodeWithTimeout({
        val result = handleNetworkResult {
            val apps =
                cleanApkPWARepository.getSearchResult(query).body()?.apps
            apps?.apply {
@@ -297,14 +294,14 @@ class FusedApiImpl @Inject constructor(
                    pwaApps.addAll(this)
                }
            }
        })
        }

        if (pwaApps.isNotEmpty() || status != ResultStatus.OK) {
        if (pwaApps.isNotEmpty() || result.getResultStatus() != ResultStatus.OK) {
            searchResult.addAll(pwaApps)
        }

        return ResultSupreme.create(
            status,
            result.getResultStatus(),
            Pair(
                filterWithKeywordSearch(
                    searchResult,
@@ -322,16 +319,17 @@ class FusedApiImpl @Inject constructor(
        searchResult: MutableList<FusedApp>,
        packageSpecificResults: ArrayList<FusedApp>
    ): ResultSupreme<Pair<List<FusedApp>, Boolean>> {
        val status = runCodeWithTimeout({
        val result = handleNetworkResult {
            cleanApkResults.addAll(getCleanAPKSearchResults(query))
        })
            cleanApkResults
        }

        if (cleanApkResults.isNotEmpty()) {
            searchResult.addAll(cleanApkResults)
        }

        return ResultSupreme.create(
            status,
            result.getResultStatus(),
            Pair(
                filterWithKeywordSearch(
                    searchResult,
@@ -351,7 +349,7 @@ class FusedApiImpl @Inject constructor(
        var gplayPackageResult: FusedApp? = null
        var cleanapkPackageResult: FusedApp? = null

        val status = runCodeWithTimeout({
        val result = handleNetworkResult {
            if (preferenceManagerModule.isGplaySelected()) {
                gplayPackageResult = getGplayPackagResult(query, authData)
            }
@@ -359,7 +357,7 @@ class FusedApiImpl @Inject constructor(
            if (preferenceManagerModule.isOpenSourceSelected()) {
                cleanapkPackageResult = getCleanApkPackageResult(query)
            }
        })
        }

        /*
         * Currently only show open source package result if exists in both fdroid and gplay.
@@ -378,10 +376,13 @@ class FusedApiImpl @Inject constructor(
         * If there was a timeout, return it and don't try to fetch anything else.
         * Also send true in the pair to signal more results being loaded.
         */
        if (status != ResultStatus.OK) {
            return ResultSupreme.create(status, Pair(packageSpecificResults, false))
        if (result.getResultStatus() != ResultStatus.OK) {
            return ResultSupreme.create(
                result.getResultStatus(),
                Pair(packageSpecificResults, false)
            )
        }
        return ResultSupreme.create(status, Pair(packageSpecificResults, true))
        return ResultSupreme.create(result.getResultStatus(), Pair(packageSpecificResults, true))
    }

    /*
@@ -446,7 +447,7 @@ class FusedApiImpl @Inject constructor(
     */
    private suspend fun getCleanapkSearchResult(packageName: String): ResultSupreme<FusedApp> {
        var fusedApp = FusedApp()
        val status = runCodeWithTimeout({
        val result = handleNetworkResult {
            val result = cleanApkAppsRepository.getSearchResult(
                packageName,
                "package_name"
@@ -455,15 +456,15 @@ class FusedApiImpl @Inject constructor(
            if (result?.apps?.isNotEmpty() == true && result.numberOfResults == 1) {
                fusedApp = result.apps[0]
            }
        })
        return ResultSupreme.create(status, fusedApp)
        }
        return ResultSupreme.create(result.getResultStatus(), fusedApp)
    }

    override suspend fun getSearchSuggestions(query: String): List<SearchSuggestEntry> {
        var searchSuggesions = listOf<SearchSuggestEntry>()
        runCodeWithTimeout({
        handleNetworkResult {
            searchSuggesions = gplayRepository.getSearchSuggestions(query)
        })
        }

        return searchSuggesions
    }
@@ -523,7 +524,7 @@ class FusedApiImpl @Inject constructor(

    override suspend fun getPWAApps(category: String): ResultSupreme<Pair<List<FusedApp>, String>> {
        val list = mutableListOf<FusedApp>()
        val status = runCodeWithTimeout({
        val result = handleNetworkResult {
            val response = getPWAAppsResponse(category)
            response?.apps?.forEach {
                it.updateStatus()
@@ -531,13 +532,13 @@ class FusedApiImpl @Inject constructor(
                it.updateFilterLevel(null)
                list.add(it)
            }
        })
        return ResultSupreme.create(status, Pair(list, ""))
        }
        return ResultSupreme.create(result.getResultStatus(), Pair(list, ""))
    }

    override suspend fun getOpenSourceApps(category: String): ResultSupreme<Pair<List<FusedApp>, String>> {
        val list = mutableListOf<FusedApp>()
        val status = runCodeWithTimeout({
        val result = handleNetworkResult {
            val response = getOpenSourceAppsResponse(category)
            response?.apps?.forEach {
                it.updateStatus()
@@ -545,8 +546,8 @@ class FusedApiImpl @Inject constructor(
                it.updateFilterLevel(null)
                list.add(it)
            }
        })
        return ResultSupreme.create(status, Pair(list, ""))
        }
        return ResultSupreme.create(result.getResultStatus(), Pair(list, ""))
    }

    /*
@@ -557,7 +558,7 @@ class FusedApiImpl @Inject constructor(
     */
    override suspend fun getCleanapkAppDetails(packageName: String): Pair<FusedApp, ResultStatus> {
        var fusedApp = FusedApp()
        val status = runCodeWithTimeout({
        val result = handleNetworkResult {
            val result = cleanApkAppsRepository.getSearchResult(
                packageName,
                "package_name"
@@ -569,8 +570,8 @@ class FusedApiImpl @Inject constructor(
                        ?: FusedApp()
            }
            fusedApp.updateFilterLevel(null)
        })
        return Pair(fusedApp, status)
        }
        return Pair(fusedApp, result.getResultStatus())
    }

    override suspend fun getApplicationDetails(
@@ -614,7 +615,7 @@ class FusedApiImpl @Inject constructor(
         * i.e. check timeout for individual package query.
         */
        for (packageName in packageNameList) {
            status = runCodeWithTimeout({
            val result = handleNetworkResult {
                cleanApkAppsRepository.getSearchResult(
                    packageName,
                    "package_name"
@@ -627,7 +628,9 @@ class FusedApiImpl @Inject constructor(
                        )
                    }
                }
            })
            }

            status = result.getResultStatus()

            /*
             * If status is not ok, immediately return.
@@ -653,7 +656,7 @@ class FusedApiImpl @Inject constructor(
        /*
         * Old code moved from getApplicationDetails()
         */
        val status = runCodeWithTimeout({
        val result = handleNetworkResult {
            gplayRepository.getAppsDetails(packageNameList).forEach { app ->
                /*
                 * Some apps are restricted to locations. Example "com.skype.m2".
@@ -670,9 +673,9 @@ class FusedApiImpl @Inject constructor(
                    )
                }
            }
        })
        }

        return Pair(fusedAppList, status)
        return Pair(fusedAppList, result.getResultStatus())
    }

    /**
@@ -691,7 +694,7 @@ class FusedApiImpl @Inject constructor(
        appList: List<App>,
    ): ResultSupreme<List<FusedApp>> {
        val filteredFusedApps = mutableListOf<FusedApp>()
        val status = runCodeWithTimeout({
        return handleNetworkResult {
            appList.forEach {
                val filter = getAppFilterLevel(it, authData)
                if (filter.isUnFiltered()) {
@@ -702,9 +705,8 @@ class FusedApiImpl @Inject constructor(
                    )
                }
            }
        })

        return ResultSupreme.create(status, filteredFusedApps)
            filteredFusedApps
        }
    }

    /**
@@ -783,7 +785,7 @@ class FusedApiImpl @Inject constructor(

        var response: FusedApp? = null

        val status = runCodeWithTimeout({
        val result = handleNetworkResult {
            response = if (origin == Origin.CLEANAPK) {
                (cleanApkAppsRepository.getAppDetails(id) as Response<Application>).body()?.app
            } else {
@@ -796,9 +798,10 @@ class FusedApiImpl @Inject constructor(
                it.updateSource()
                it.updateFilterLevel(authData)
            }
        })
            response
        }

        return Pair(response ?: FusedApp(), status)
        return Pair(result.data ?: FusedApp(), result.getResultStatus())
    }

    /*
@@ -836,9 +839,9 @@ class FusedApiImpl @Inject constructor(
            val gplayCategoryResult = fetchGplayCategories(
                type,
            )
            categoriesList.addAll(gplayCategoryResult.second)
            apiStatus = gplayCategoryResult.first
            errorApplicationCategory = gplayCategoryResult.third
            categoriesList.addAll(gplayCategoryResult.data ?: listOf())
            apiStatus = gplayCategoryResult.getResultStatus()
            errorApplicationCategory = APP_TYPE_ANY
        }

        return Pair(apiStatus, errorApplicationCategory)
@@ -846,34 +849,25 @@ class FusedApiImpl @Inject constructor(

    private suspend fun fetchGplayCategories(
        type: CategoryType,
    ): Triple<ResultStatus, List<FusedCategory>, String> {
        var errorApplicationCategory = ""
        var apiStatus = ResultStatus.OK
    ): ResultSupreme<List<FusedCategory>> {
        val categoryList = mutableListOf<FusedCategory>()
        runCodeWithTimeout({

        return handleNetworkResult {
            val playResponse = gplayRepository.getCategories(type).map { app ->
                val category = app.transformToFusedCategory()
                updateCategoryDrawable(category)
                category
            }
            categoryList.addAll(playResponse)
        }, {
            errorApplicationCategory = APP_TYPE_ANY
            apiStatus = ResultStatus.TIMEOUT
        }, {
            errorApplicationCategory = APP_TYPE_ANY
            apiStatus = ResultStatus.UNKNOWN
        })
        return Triple(apiStatus, categoryList, errorApplicationCategory)
            categoryList
        }
    }

    private suspend fun fetchPWACategories(
        type: CategoryType,
    ): Triple<ResultStatus, List<FusedCategory>, String> {
        var errorApplicationCategory = ""
        var apiStatus: ResultStatus = ResultStatus.OK
        val fusedCategoriesList = mutableListOf<FusedCategory>()
        runCodeWithTimeout({
        val result = handleNetworkResult {
            getPWAsCategories()?.let {
                fusedCategoriesList.addAll(
                    getFusedCategoryBasedOnCategoryType(
@@ -881,23 +875,16 @@ class FusedApiImpl @Inject constructor(
                    )
                )
            }
        }, {
            errorApplicationCategory = APP_TYPE_PWA
            apiStatus = ResultStatus.TIMEOUT
        }, {
            errorApplicationCategory = APP_TYPE_PWA
            apiStatus = ResultStatus.UNKNOWN
        })
        return Triple(apiStatus, fusedCategoriesList, errorApplicationCategory)
        }

        return Triple(result.getResultStatus(), fusedCategoriesList, APP_TYPE_PWA)
    }

    private suspend fun fetchOpenSourceCategories(
        type: CategoryType,
    ): Triple<ResultStatus, List<FusedCategory>, String> {
        var errorApplicationCategory = ""
        var apiStatus: ResultStatus = ResultStatus.OK
        val fusedCategoryList = mutableListOf<FusedCategory>()
        runCodeWithTimeout({
        val result = handleNetworkResult {
            getOpenSourceCategories()?.let {
                fusedCategoryList.addAll(
                    getFusedCategoryBasedOnCategoryType(
@@ -907,14 +894,9 @@ class FusedApiImpl @Inject constructor(
                    )
                )
            }
        }, {
            errorApplicationCategory = APP_TYPE_OPEN
            apiStatus = ResultStatus.TIMEOUT
        }, {
            errorApplicationCategory = APP_TYPE_OPEN
            apiStatus = ResultStatus.UNKNOWN
        })
        return Triple(apiStatus, fusedCategoryList, errorApplicationCategory)
        }

        return Triple(result.getResultStatus(), fusedCategoryList, APP_TYPE_OPEN)
    }

    /**
@@ -956,9 +938,8 @@ class FusedApiImpl @Inject constructor(
    }

    private fun getCategoryIconName(category: FusedCategory): String {
        var categoryTitle = if (category.tag.getOperationalTag()
            .contentEquals(AppTag.GPlay().getOperationalTag())
        ) category.id else category.title
        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")
@@ -1082,12 +1063,12 @@ class FusedApiImpl @Inject constructor(
        query: String,
        nextPageSubBundle: Set<SearchBundle.SubBundle>?
    ): GplaySearchResult {
        try {
        return handleNetworkResult {
            val searchResults =
                gplayRepository.getSearchResult(query, nextPageSubBundle?.toMutableSet())

            if (!preferenceManagerModule.isGplaySelected()) {
                return ResultSupreme.Error(ERROR_GPLAY_SOURCE_NOT_SELECTED)
                return@handleNetworkResult Pair(listOf<FusedApp>(), setOf<SearchBundle.SubBundle>())
            }

            val fusedAppList =
@@ -1097,20 +1078,7 @@ 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}"

            val exception = GPlayException(e.status == 408, message)
            return ResultSupreme.Error(message, exception)
        } catch (e: Exception) {
            val exception =
                GPlayException(e is SocketTimeoutException, e.localizedMessage)

            return ResultSupreme.Error(e.localizedMessage ?: "", exception)
            return@handleNetworkResult Pair(fusedAppList.toList(), searchResults.second.toSet())
        }
    }

@@ -1416,7 +1384,7 @@ class FusedApiImpl @Inject constructor(
        var fusedAppList: MutableList<FusedApp> = mutableListOf()
        var nextPageUrl = ""

        val status = runCodeWithTimeout({
        return handleNetworkResult {
            val streamCluster =
                gplayRepository.getAppsByCategory(category, pageUrl) as StreamCluster

@@ -1429,8 +1397,7 @@ class FusedApiImpl @Inject constructor(
            if (!nextPageUrl.isNullOrEmpty()) {
                fusedAppList.add(FusedApp(isPlaceHolder = true))
            }
        })

        return ResultSupreme.create(status, Pair(fusedAppList, nextPageUrl))
            Pair(fusedAppList, nextPageUrl)
        }
    }
}
+6 −25
Original line number Diff line number Diff line
@@ -40,7 +40,6 @@ import okhttp3.Response
import timber.log.Timber
import java.io.IOException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.util.concurrent.TimeUnit
import javax.inject.Inject

@@ -58,6 +57,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()
@@ -163,32 +163,16 @@ class GPlayHttpClient @Inject constructor(
            val call = okHttpClient.newCall(request)
            response = call.execute()
            buildPlayResponse(response)
        } 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)) {
        } catch (e: GplayHttpRequestException) {
            throw e
            }

            when (e) {
                is UnknownHostException,
                is SocketTimeoutException -> handleExceptionOnGooglePlayRequest(e)

                else -> handleExceptionOnGooglePlayRequest(e)
            }
        } catch (e: Exception) {
            val status = if (e is SocketTimeoutException) STATUS_CODE_TIMEOUT else -1
            throw GplayHttpRequestException(status, e.localizedMessage ?: "")
        } finally {
            response?.close()
        }
    }

    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 {
        val urlBuilder = url.toHttpUrl().newBuilder()
        params.forEach {
@@ -222,10 +206,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