Loading app/src/main/java/foundation/e/apps/MainActivity.kt +7 −1 Original line number Diff line number Diff line Loading @@ -90,6 +90,7 @@ class MainActivity : AppCompatActivity() { User.ANONYMOUS -> { if (viewModel.authDataJson.value.isNullOrEmpty() && !viewModel.authRequestRunning) { Log.d(TAG, "Fetching new authentication data") viewModel.setFirstTokenFetchTime() viewModel.getAuthData() } } Loading @@ -99,6 +100,7 @@ class MainActivity : AppCompatActivity() { User.GOOGLE -> { if (viewModel.authData.value == null && !viewModel.authRequestRunning) { Log.d(TAG, "Fetching new authentication data") viewModel.setFirstTokenFetchTime() signInViewModel.fetchAuthData() } } Loading Loading @@ -134,7 +136,11 @@ class MainActivity : AppCompatActivity() { if (it != true) { Log.d(TAG, "Authentication data validation failed!") viewModel.destroyCredentials { user -> if (viewModel.isTimeEligibleForTokenRefresh()) { generateAuthDataBasedOnUserType(user) } else { Log.d(TAG, "Timeout validating auth data!") } } } else { Log.d(TAG, "Authentication data is valid!") Loading app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +138 −1 Original line number Diff line number Diff line Loading @@ -18,14 +18,18 @@ package foundation.e.apps import android.app.AlertDialog import android.app.Activity import android.content.Context import android.content.DialogInterface import android.graphics.Bitmap import android.os.Build import android.os.SystemClock import android.util.Base64 import android.util.Log import android.view.KeyEvent import android.widget.ImageView import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData Loading @@ -37,16 +41,19 @@ import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.exceptions.ApiException import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.api.fused.FusedAPIImpl import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.manager.database.fusedDownload.FusedDownload import foundation.e.apps.manager.fused.FusedManagerRepository import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.manager.workmanager.InstallWorkManager import foundation.e.apps.settings.SettingsFragment import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.enums.User import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.DataStoreModule import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch Loading Loading @@ -76,6 +83,116 @@ class MainActivityViewModel @Inject constructor( val purchaseDeclined: MutableLiveData<String> = MutableLiveData() var authRequestRunning = false /* * Store the time when auth data is fetched for the first time. * If we try to fetch auth data after timeout, then don't allow it. * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ var firstAuthDataFetchTime = 0L /* * Alert dialog to show to user if App Lounge times out. * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ private lateinit var timeoutAlertDialog: AlertDialog /** * Display timeout alert dialog. * * @param activity Activity class. Basically the MainActivity. * @param positiveButtonBlock Code block when "Retry" is pressed. * @param openSettings Code block when "Open Settings" button is pressed. * This should open the [SettingsFragment] fragment. * @param applicationTypeFromPreferences Application type string, can be one of * [FusedAPIImpl.APP_TYPE_ANY], [FusedAPIImpl.APP_TYPE_OPEN], [FusedAPIImpl.APP_TYPE_PWA] */ fun displayTimeoutAlertDialog( activity: Activity, positiveButtonBlock: () -> Unit, openSettings: () -> Unit, applicationTypeFromPreferences: String, ) { if (!this::timeoutAlertDialog.isInitialized) { timeoutAlertDialog = AlertDialog.Builder(activity).apply { setTitle(R.string.timeout_title) /* * Prevent dismissing the dialog from pressing outside as it will only * show a blank screen below the dialog. */ setCancelable(false) /* * If user presses back button to close the dialog without selecting anything, * close App Lounge. */ setOnKeyListener { dialog, keyCode, _ -> if (keyCode == KeyEvent.KEYCODE_BACK) { dialog.dismiss() activity.finish() } true } }.create() } timeoutAlertDialog.apply { /* * Set retry button. */ setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.retry)) {_, _ -> positiveButtonBlock() } /* * Set message based on apps from GPlay of cleanapk. */ setMessage( activity.getString( when (applicationTypeFromPreferences) { FusedAPIImpl.APP_TYPE_ANY -> R.string.timeout_desc_gplay else -> R.string.timeout_desc_cleanapk } ) ) /* * Show "Open Setting" only for GPlay apps. */ if (applicationTypeFromPreferences == FusedAPIImpl.APP_TYPE_ANY) { setButton( DialogInterface.BUTTON_NEUTRAL, activity.getString(R.string.open_settings) ) { _, _ -> openSettings() } } } timeoutAlertDialog.show() } /** * Returns true if [timeoutAlertDialog] is displaying. * Returs false if it is not initialised. */ fun isTimeoutDialogDisplayed(): Boolean { return if (this::timeoutAlertDialog.isInitialized) { timeoutAlertDialog.isShowing } else false } /** * Dismisses the [timeoutAlertDialog] if it is being displayed. * Does nothing if it is not being displayed. * Caller need not check if the dialog is being displayed. */ fun dismissTimeoutDialog() { if (isTimeoutDialogDisplayed()) { try { timeoutAlertDialog.dismiss() } catch (_: Exception) {} } } // Downloads val downloadList = fusedManagerRepository.getDownloadLiveList() var installInProgress = false Loading @@ -92,6 +209,26 @@ class MainActivityViewModel @Inject constructor( private const val TAG = "MainActivityViewModel" } fun setFirstTokenFetchTime() { firstAuthDataFetchTime = SystemClock.uptimeMillis() } fun isTimeEligibleForTokenRefresh(): Boolean { return (SystemClock.uptimeMillis() - firstAuthDataFetchTime) <= timeoutDurationInMillis } /* * This method resets the last recorded token fetch time. * Then it posts authValidity as false. This causes the observer in MainActivity to destroyCredentials * and fetch new token. * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ fun retryFetchingTokenAfterTimeout() { setFirstTokenFetchTime() authValidity.postValue(false) } fun getAuthData() { if (!authRequestRunning) { authRequestRunning = true Loading app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +84 −22 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ package foundation.e.apps.api.fused import android.content.Context import android.text.format.Formatter import android.util.Log import com.aurora.gplayapi.Constants import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App Loading @@ -46,8 +47,11 @@ import foundation.e.apps.utils.enums.AppTag import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.PWAManagerModule import foundation.e.apps.utils.modules.PreferenceManagerModule import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.withTimeout import javax.inject.Inject import javax.inject.Singleton Loading @@ -63,21 +67,72 @@ class FusedAPIImpl @Inject constructor( companion object { private const val CATEGORY_TITLE_REPLACEABLE_CONJUNCTION = "&" private const val APP_TYPE_ANY = "any" private const val APP_TYPE_OPEN = "open" private const val APP_TYPE_PWA = "pwa" /* * Removing "private" access specifier to allow access in * MainActivityViewModel.timeoutAlertDialog * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ const val APP_TYPE_ANY = "any" const val APP_TYPE_OPEN = "open" const val APP_TYPE_PWA = "pwa" private const val CATEGORY_OPEN_GAMES_ID = "game_open_games" private const val CATEGORY_OPEN_GAMES_TITLE = "Open games" } private var TAG = FusedAPIImpl::class.java.simpleName suspend fun getHomeScreenData(authData: AuthData): List<FusedHome> { val list = mutableListOf<FusedHome>() /** * Pass application source type along with list of apps. * Application source type may change in case of timeout of GPlay/cleanapk api. * * The second item of the Pair can be one of [APP_TYPE_ANY], [APP_TYPE_OPEN], [APP_TYPE_PWA]. * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ suspend fun getHomeScreenData(authData: AuthData): Pair<List<FusedHome>, String> { val preferredApplicationType = preferenceManagerModule.preferredApplicationType() val initialData = getHomeScreenDataBasedOnApplicationType(authData, preferredApplicationType) if (isFusedHomesEmpty(initialData.first)) { Log.d(TAG, "Received empty home data.") } return initialData } if (preferredApplicationType != APP_TYPE_ANY) { val response = if (preferredApplicationType == APP_TYPE_OPEN) { /** * Check if list in all the FusedHome is empty. * If any list is not empty, send false. * Else (if all lists are empty) send true. */ fun isFusedHomesEmpty(fusedHomes: List<FusedHome>): Boolean { fusedHomes.forEach { if (it.list.isNotEmpty()) return false } return true } /* * Offload fetching application to a different method to dynamically fallback to a different * app source if the user selected app source times out. * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ private suspend fun getHomeScreenDataBasedOnApplicationType( authData: AuthData, applicationType: String ): Pair<List<FusedHome>, String> { val list = mutableListOf<FusedHome>() try { /* * Each category of home apps (example "Top Free Apps") will have its own timeout. * Fetching 6 such categories will have a total timeout to 2 mins 30 seconds * (considering each category having 25 seconds timeout). * * To prevent waiting so long and fail early, use withTimeout{}. */ withTimeout(timeoutDurationInMillis) { if (applicationType != APP_TYPE_ANY) { val response = if (applicationType == APP_TYPE_OPEN) { cleanAPKRepository.getHomeScreenData( CleanAPKInterface.APP_TYPE_ANY, CleanAPKInterface.APP_SOURCE_FOSS Loading @@ -89,12 +144,19 @@ class FusedAPIImpl @Inject constructor( ).body() } response?.home?.let { list.addAll(generateCleanAPKHome(it, preferredApplicationType)) list.addAll(generateCleanAPKHome(it, applicationType)) } } else { list.addAll(fetchGPlayHome(authData)) } return list } } catch (e: TimeoutCancellationException) { e.printStackTrace() Log.d(TAG, "Timed out fetching home data for type: $applicationType") } catch (e: Exception) { e.printStackTrace() } return Pair(list, applicationType) } suspend fun getCategoriesList(type: Category.Type, authData: AuthData): List<FusedCategory> { Loading app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +5 −1 Original line number Diff line number Diff line Loading @@ -34,10 +34,14 @@ import javax.inject.Singleton class FusedAPIRepository @Inject constructor( private val fusedAPIImpl: FusedAPIImpl ) { suspend fun getHomeScreenData(authData: AuthData): List<FusedHome> { suspend fun getHomeScreenData(authData: AuthData): Pair<List<FusedHome>, String> { return fusedAPIImpl.getHomeScreenData(authData) } fun isFusedHomesEmpty(fusedHomes: List<FusedHome>): Boolean { return fusedAPIImpl.isFusedHomesEmpty(fusedHomes) } suspend fun validateAuthData(authData: AuthData): Boolean { return fusedAPIImpl.validateAuthData(authData) } Loading app/src/main/java/foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt +6 −4 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.util.Log import com.aurora.gplayapi.data.models.PlayResponse import com.aurora.gplayapi.network.IHttpClient import foundation.e.apps.BuildConfig import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import okhttp3.Cache import okhttp3.Headers.Companion.toHeaders import okhttp3.HttpUrl Loading Loading @@ -51,10 +52,11 @@ class GPlayHttpClient @Inject constructor( } private val okHttpClient = OkHttpClient().newBuilder() .connectTimeout(25, TimeUnit.SECONDS) .readTimeout(25, TimeUnit.SECONDS) .writeTimeout(25, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .connectTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) .readTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) .writeTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) .callTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) .retryOnConnectionFailure(false) .followRedirects(true) .followSslRedirects(true) .cache(cache) Loading Loading
app/src/main/java/foundation/e/apps/MainActivity.kt +7 −1 Original line number Diff line number Diff line Loading @@ -90,6 +90,7 @@ class MainActivity : AppCompatActivity() { User.ANONYMOUS -> { if (viewModel.authDataJson.value.isNullOrEmpty() && !viewModel.authRequestRunning) { Log.d(TAG, "Fetching new authentication data") viewModel.setFirstTokenFetchTime() viewModel.getAuthData() } } Loading @@ -99,6 +100,7 @@ class MainActivity : AppCompatActivity() { User.GOOGLE -> { if (viewModel.authData.value == null && !viewModel.authRequestRunning) { Log.d(TAG, "Fetching new authentication data") viewModel.setFirstTokenFetchTime() signInViewModel.fetchAuthData() } } Loading Loading @@ -134,7 +136,11 @@ class MainActivity : AppCompatActivity() { if (it != true) { Log.d(TAG, "Authentication data validation failed!") viewModel.destroyCredentials { user -> if (viewModel.isTimeEligibleForTokenRefresh()) { generateAuthDataBasedOnUserType(user) } else { Log.d(TAG, "Timeout validating auth data!") } } } else { Log.d(TAG, "Authentication data is valid!") Loading
app/src/main/java/foundation/e/apps/MainActivityViewModel.kt +138 −1 Original line number Diff line number Diff line Loading @@ -18,14 +18,18 @@ package foundation.e.apps import android.app.AlertDialog import android.app.Activity import android.content.Context import android.content.DialogInterface import android.graphics.Bitmap import android.os.Build import android.os.SystemClock import android.util.Base64 import android.util.Log import android.view.KeyEvent import android.widget.ImageView import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData Loading @@ -37,16 +41,19 @@ import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.exceptions.ApiException import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.api.fused.FusedAPIImpl import foundation.e.apps.api.fused.FusedAPIRepository import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.manager.database.fusedDownload.FusedDownload import foundation.e.apps.manager.fused.FusedManagerRepository import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.manager.workmanager.InstallWorkManager import foundation.e.apps.settings.SettingsFragment import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.enums.User import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.DataStoreModule import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch Loading Loading @@ -76,6 +83,116 @@ class MainActivityViewModel @Inject constructor( val purchaseDeclined: MutableLiveData<String> = MutableLiveData() var authRequestRunning = false /* * Store the time when auth data is fetched for the first time. * If we try to fetch auth data after timeout, then don't allow it. * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ var firstAuthDataFetchTime = 0L /* * Alert dialog to show to user if App Lounge times out. * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ private lateinit var timeoutAlertDialog: AlertDialog /** * Display timeout alert dialog. * * @param activity Activity class. Basically the MainActivity. * @param positiveButtonBlock Code block when "Retry" is pressed. * @param openSettings Code block when "Open Settings" button is pressed. * This should open the [SettingsFragment] fragment. * @param applicationTypeFromPreferences Application type string, can be one of * [FusedAPIImpl.APP_TYPE_ANY], [FusedAPIImpl.APP_TYPE_OPEN], [FusedAPIImpl.APP_TYPE_PWA] */ fun displayTimeoutAlertDialog( activity: Activity, positiveButtonBlock: () -> Unit, openSettings: () -> Unit, applicationTypeFromPreferences: String, ) { if (!this::timeoutAlertDialog.isInitialized) { timeoutAlertDialog = AlertDialog.Builder(activity).apply { setTitle(R.string.timeout_title) /* * Prevent dismissing the dialog from pressing outside as it will only * show a blank screen below the dialog. */ setCancelable(false) /* * If user presses back button to close the dialog without selecting anything, * close App Lounge. */ setOnKeyListener { dialog, keyCode, _ -> if (keyCode == KeyEvent.KEYCODE_BACK) { dialog.dismiss() activity.finish() } true } }.create() } timeoutAlertDialog.apply { /* * Set retry button. */ setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.retry)) {_, _ -> positiveButtonBlock() } /* * Set message based on apps from GPlay of cleanapk. */ setMessage( activity.getString( when (applicationTypeFromPreferences) { FusedAPIImpl.APP_TYPE_ANY -> R.string.timeout_desc_gplay else -> R.string.timeout_desc_cleanapk } ) ) /* * Show "Open Setting" only for GPlay apps. */ if (applicationTypeFromPreferences == FusedAPIImpl.APP_TYPE_ANY) { setButton( DialogInterface.BUTTON_NEUTRAL, activity.getString(R.string.open_settings) ) { _, _ -> openSettings() } } } timeoutAlertDialog.show() } /** * Returns true if [timeoutAlertDialog] is displaying. * Returs false if it is not initialised. */ fun isTimeoutDialogDisplayed(): Boolean { return if (this::timeoutAlertDialog.isInitialized) { timeoutAlertDialog.isShowing } else false } /** * Dismisses the [timeoutAlertDialog] if it is being displayed. * Does nothing if it is not being displayed. * Caller need not check if the dialog is being displayed. */ fun dismissTimeoutDialog() { if (isTimeoutDialogDisplayed()) { try { timeoutAlertDialog.dismiss() } catch (_: Exception) {} } } // Downloads val downloadList = fusedManagerRepository.getDownloadLiveList() var installInProgress = false Loading @@ -92,6 +209,26 @@ class MainActivityViewModel @Inject constructor( private const val TAG = "MainActivityViewModel" } fun setFirstTokenFetchTime() { firstAuthDataFetchTime = SystemClock.uptimeMillis() } fun isTimeEligibleForTokenRefresh(): Boolean { return (SystemClock.uptimeMillis() - firstAuthDataFetchTime) <= timeoutDurationInMillis } /* * This method resets the last recorded token fetch time. * Then it posts authValidity as false. This causes the observer in MainActivity to destroyCredentials * and fetch new token. * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ fun retryFetchingTokenAfterTimeout() { setFirstTokenFetchTime() authValidity.postValue(false) } fun getAuthData() { if (!authRequestRunning) { authRequestRunning = true Loading
app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt +84 −22 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ package foundation.e.apps.api.fused import android.content.Context import android.text.format.Formatter import android.util.Log import com.aurora.gplayapi.Constants import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App Loading @@ -46,8 +47,11 @@ import foundation.e.apps.utils.enums.AppTag import foundation.e.apps.utils.enums.Origin import foundation.e.apps.utils.enums.Status import foundation.e.apps.utils.enums.Type import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import foundation.e.apps.utils.modules.PWAManagerModule import foundation.e.apps.utils.modules.PreferenceManagerModule import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.withTimeout import javax.inject.Inject import javax.inject.Singleton Loading @@ -63,21 +67,72 @@ class FusedAPIImpl @Inject constructor( companion object { private const val CATEGORY_TITLE_REPLACEABLE_CONJUNCTION = "&" private const val APP_TYPE_ANY = "any" private const val APP_TYPE_OPEN = "open" private const val APP_TYPE_PWA = "pwa" /* * Removing "private" access specifier to allow access in * MainActivityViewModel.timeoutAlertDialog * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ const val APP_TYPE_ANY = "any" const val APP_TYPE_OPEN = "open" const val APP_TYPE_PWA = "pwa" private const val CATEGORY_OPEN_GAMES_ID = "game_open_games" private const val CATEGORY_OPEN_GAMES_TITLE = "Open games" } private var TAG = FusedAPIImpl::class.java.simpleName suspend fun getHomeScreenData(authData: AuthData): List<FusedHome> { val list = mutableListOf<FusedHome>() /** * Pass application source type along with list of apps. * Application source type may change in case of timeout of GPlay/cleanapk api. * * The second item of the Pair can be one of [APP_TYPE_ANY], [APP_TYPE_OPEN], [APP_TYPE_PWA]. * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ suspend fun getHomeScreenData(authData: AuthData): Pair<List<FusedHome>, String> { val preferredApplicationType = preferenceManagerModule.preferredApplicationType() val initialData = getHomeScreenDataBasedOnApplicationType(authData, preferredApplicationType) if (isFusedHomesEmpty(initialData.first)) { Log.d(TAG, "Received empty home data.") } return initialData } if (preferredApplicationType != APP_TYPE_ANY) { val response = if (preferredApplicationType == APP_TYPE_OPEN) { /** * Check if list in all the FusedHome is empty. * If any list is not empty, send false. * Else (if all lists are empty) send true. */ fun isFusedHomesEmpty(fusedHomes: List<FusedHome>): Boolean { fusedHomes.forEach { if (it.list.isNotEmpty()) return false } return true } /* * Offload fetching application to a different method to dynamically fallback to a different * app source if the user selected app source times out. * * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 */ private suspend fun getHomeScreenDataBasedOnApplicationType( authData: AuthData, applicationType: String ): Pair<List<FusedHome>, String> { val list = mutableListOf<FusedHome>() try { /* * Each category of home apps (example "Top Free Apps") will have its own timeout. * Fetching 6 such categories will have a total timeout to 2 mins 30 seconds * (considering each category having 25 seconds timeout). * * To prevent waiting so long and fail early, use withTimeout{}. */ withTimeout(timeoutDurationInMillis) { if (applicationType != APP_TYPE_ANY) { val response = if (applicationType == APP_TYPE_OPEN) { cleanAPKRepository.getHomeScreenData( CleanAPKInterface.APP_TYPE_ANY, CleanAPKInterface.APP_SOURCE_FOSS Loading @@ -89,12 +144,19 @@ class FusedAPIImpl @Inject constructor( ).body() } response?.home?.let { list.addAll(generateCleanAPKHome(it, preferredApplicationType)) list.addAll(generateCleanAPKHome(it, applicationType)) } } else { list.addAll(fetchGPlayHome(authData)) } return list } } catch (e: TimeoutCancellationException) { e.printStackTrace() Log.d(TAG, "Timed out fetching home data for type: $applicationType") } catch (e: Exception) { e.printStackTrace() } return Pair(list, applicationType) } suspend fun getCategoriesList(type: Category.Type, authData: AuthData): List<FusedCategory> { Loading
app/src/main/java/foundation/e/apps/api/fused/FusedAPIRepository.kt +5 −1 Original line number Diff line number Diff line Loading @@ -34,10 +34,14 @@ import javax.inject.Singleton class FusedAPIRepository @Inject constructor( private val fusedAPIImpl: FusedAPIImpl ) { suspend fun getHomeScreenData(authData: AuthData): List<FusedHome> { suspend fun getHomeScreenData(authData: AuthData): Pair<List<FusedHome>, String> { return fusedAPIImpl.getHomeScreenData(authData) } fun isFusedHomesEmpty(fusedHomes: List<FusedHome>): Boolean { return fusedAPIImpl.isFusedHomesEmpty(fusedHomes) } suspend fun validateAuthData(authData: AuthData): Boolean { return fusedAPIImpl.validateAuthData(authData) } Loading
app/src/main/java/foundation/e/apps/api/gplay/utils/GPlayHttpClient.kt +6 −4 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.util.Log import com.aurora.gplayapi.data.models.PlayResponse import com.aurora.gplayapi.network.IHttpClient import foundation.e.apps.BuildConfig import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis import okhttp3.Cache import okhttp3.Headers.Companion.toHeaders import okhttp3.HttpUrl Loading Loading @@ -51,10 +52,11 @@ class GPlayHttpClient @Inject constructor( } private val okHttpClient = OkHttpClient().newBuilder() .connectTimeout(25, TimeUnit.SECONDS) .readTimeout(25, TimeUnit.SECONDS) .writeTimeout(25, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .connectTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) .readTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) .writeTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) .callTimeout(timeoutDurationInMillis, TimeUnit.MILLISECONDS) .retryOnConnectionFailure(false) .followRedirects(true) .followSslRedirects(true) .cache(cache) Loading