Loading app/src/main/java/foundation/e/apps/data/NetworkHandler.kt +41 −0 Original line number Diff line number Diff line Loading @@ -21,12 +21,17 @@ package foundation.e.apps.data import foundation.e.apps.data.playstore.utils.GPlayHttpClient import foundation.e.apps.data.playstore.utils.GplayHttpRequestException import foundation.e.apps.data.login.exceptions.GPlayException import kotlinx.coroutines.delay import timber.log.Timber 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!" private const val REGEX_CONTAIN_429_OR_401 = "429|401" private const val MAX_RETRY_DELAY_IN_SECONDS = 300 private const val ONE_SECOND_IN_MILLIS = 1000L suspend fun <T> handleNetworkResult(call: suspend () -> T): ResultSupreme<T> { return try { Loading Loading @@ -71,3 +76,39 @@ private fun extractErrorMessage(e: Exception): String { } return (e.localizedMessage?.ifBlank { ERROR_GPLAY_API } ?: ERROR_GPLAY_API) + " $STATUS $status" } suspend fun <T> retryWithBackoff(operation: suspend () -> T, retryDelayInSecond: Int = -1): T? { try { if (retryDelayInSecond > 0) { delay(ONE_SECOND_IN_MILLIS * retryDelayInSecond) } val result = operation() if (shouldRetry(result, retryDelayInSecond)) { Timber.w("Retrying...: $retryDelayInSecond") return retryWithBackoff(operation, calculateRetryDelay(retryDelayInSecond)) } return result } catch (e: Exception) { if (retryDelayInSecond < MAX_RETRY_DELAY_IN_SECONDS) { return retryWithBackoff(operation, calculateRetryDelay(retryDelayInSecond)) } } return null } private fun calculateRetryDelay(retryDelayInSecond: Int) = if (retryDelayInSecond < 0) 5 else retryDelayInSecond * 2 private fun <T> shouldRetry(result: T, retryDelayInSecond: Int) = result is ResultSupreme<*> && !result.isSuccess() && retryDelayInSecond < MAX_RETRY_DELAY_IN_SECONDS && isExceptionAllowedToRetry(result.exception) private fun isExceptionAllowedToRetry(exception: Exception?): Boolean { val message = exception?.message return message?.contains(Regex(REGEX_CONTAIN_429_OR_401)) != true // Here, (value != true) is used, because value can be null also and we want to allow retry for null message } app/src/main/java/foundation/e/apps/data/login/PlayStoreAuthenticator.kt +11 −9 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import foundation.e.apps.data.login.api.PlayStoreLoginManagerFactory import foundation.e.apps.data.login.api.PlayStoreLoginManager import foundation.e.apps.data.login.api.GoogleLoginManager import foundation.e.apps.data.login.api.PlayStoreLoginWrapper import foundation.e.apps.data.retryWithBackoff import timber.log.Timber import java.util.Locale import javax.inject.Inject Loading Loading @@ -77,25 +78,26 @@ class PlayStoreAuthenticator @Inject constructor( override suspend fun login(): AuthObject.GPlayAuth { val savedAuth = getSavedAuthData() val authData = ( savedAuth ?: run { val authData = savedAuth ?: run { // if no saved data, then generate new auth data. generateAuthData().let { retryWithBackoff({ generateAuthData() }).let { result -> result?.let { if (it.isSuccess()) it.data!! else return AuthObject.GPlayAuth(it, user) } } ) } val formattedAuthData = formatAuthData(authData) formattedAuthData.locale = locale val formattedAuthData = authData?.let { formatAuthData(it) } formattedAuthData?.locale = locale val result: ResultSupreme<AuthData?> = ResultSupreme.create( status = ResultStatus.OK, data = formattedAuthData ) result.otherPayload = formattedAuthData.email result.otherPayload = formattedAuthData?.email if (savedAuth == null) { if (savedAuth == null && formattedAuthData != null) { saveAuthData(formattedAuthData) } Loading Loading
app/src/main/java/foundation/e/apps/data/NetworkHandler.kt +41 −0 Original line number Diff line number Diff line Loading @@ -21,12 +21,17 @@ package foundation.e.apps.data import foundation.e.apps.data.playstore.utils.GPlayHttpClient import foundation.e.apps.data.playstore.utils.GplayHttpRequestException import foundation.e.apps.data.login.exceptions.GPlayException import kotlinx.coroutines.delay import timber.log.Timber 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!" private const val REGEX_CONTAIN_429_OR_401 = "429|401" private const val MAX_RETRY_DELAY_IN_SECONDS = 300 private const val ONE_SECOND_IN_MILLIS = 1000L suspend fun <T> handleNetworkResult(call: suspend () -> T): ResultSupreme<T> { return try { Loading Loading @@ -71,3 +76,39 @@ private fun extractErrorMessage(e: Exception): String { } return (e.localizedMessage?.ifBlank { ERROR_GPLAY_API } ?: ERROR_GPLAY_API) + " $STATUS $status" } suspend fun <T> retryWithBackoff(operation: suspend () -> T, retryDelayInSecond: Int = -1): T? { try { if (retryDelayInSecond > 0) { delay(ONE_SECOND_IN_MILLIS * retryDelayInSecond) } val result = operation() if (shouldRetry(result, retryDelayInSecond)) { Timber.w("Retrying...: $retryDelayInSecond") return retryWithBackoff(operation, calculateRetryDelay(retryDelayInSecond)) } return result } catch (e: Exception) { if (retryDelayInSecond < MAX_RETRY_DELAY_IN_SECONDS) { return retryWithBackoff(operation, calculateRetryDelay(retryDelayInSecond)) } } return null } private fun calculateRetryDelay(retryDelayInSecond: Int) = if (retryDelayInSecond < 0) 5 else retryDelayInSecond * 2 private fun <T> shouldRetry(result: T, retryDelayInSecond: Int) = result is ResultSupreme<*> && !result.isSuccess() && retryDelayInSecond < MAX_RETRY_DELAY_IN_SECONDS && isExceptionAllowedToRetry(result.exception) private fun isExceptionAllowedToRetry(exception: Exception?): Boolean { val message = exception?.message return message?.contains(Regex(REGEX_CONTAIN_429_OR_401)) != true // Here, (value != true) is used, because value can be null also and we want to allow retry for null message }
app/src/main/java/foundation/e/apps/data/login/PlayStoreAuthenticator.kt +11 −9 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import foundation.e.apps.data.login.api.PlayStoreLoginManagerFactory import foundation.e.apps.data.login.api.PlayStoreLoginManager import foundation.e.apps.data.login.api.GoogleLoginManager import foundation.e.apps.data.login.api.PlayStoreLoginWrapper import foundation.e.apps.data.retryWithBackoff import timber.log.Timber import java.util.Locale import javax.inject.Inject Loading Loading @@ -77,25 +78,26 @@ class PlayStoreAuthenticator @Inject constructor( override suspend fun login(): AuthObject.GPlayAuth { val savedAuth = getSavedAuthData() val authData = ( savedAuth ?: run { val authData = savedAuth ?: run { // if no saved data, then generate new auth data. generateAuthData().let { retryWithBackoff({ generateAuthData() }).let { result -> result?.let { if (it.isSuccess()) it.data!! else return AuthObject.GPlayAuth(it, user) } } ) } val formattedAuthData = formatAuthData(authData) formattedAuthData.locale = locale val formattedAuthData = authData?.let { formatAuthData(it) } formattedAuthData?.locale = locale val result: ResultSupreme<AuthData?> = ResultSupreme.create( status = ResultStatus.OK, data = formattedAuthData ) result.otherPayload = formattedAuthData.email result.otherPayload = formattedAuthData?.email if (savedAuth == null) { if (savedAuth == null && formattedAuthData != null) { saveAuthData(formattedAuthData) } Loading