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

Commit d9195749 authored by Sayantan Roychowdhury's avatar Sayantan Roychowdhury
Browse files

Issue 5709: Fix google sign out if network is unreachable

parent b96e25f4
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.os.Environment
import android.os.StatFs
import android.os.storage.StorageManager
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
@@ -111,6 +112,10 @@ class MainActivity : AppCompatActivity() {
            }
        }

        viewModel.errorAuthResponse.observe(this) {
            onSignInError()
        }

        viewModel.authValidity.observe(this) {
            viewModel.handleAuthValidity(it) {
                Timber.d("Timeout validating auth data!")
@@ -281,6 +286,19 @@ class MainActivity : AppCompatActivity() {
        }
    }

    private fun onSignInError() {
        AlertDialog.Builder(this).apply {
            setTitle(R.string.sign_in_failed_title)
            setMessage(R.string.sign_in_failed_desc)
            setPositiveButton(R.string.retry) {_ ,_ ->
                viewModel.retryFetchingTokenAfterTimeout()
            }
            setNegativeButton(R.string.logout) { _, _ ->
                viewModel.postFalseAuthValidity()
            }
        }.show()
    }

    private fun getAvailableInternalMemorySize(): Long {
        val path: File = Environment.getDataDirectory()
        val stat = StatFs(path.path)
+72 −6
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import androidx.lifecycle.asLiveData
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.PlayResponse
import com.aurora.gplayapi.exceptions.ApiException
import com.google.gson.Gson
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -42,6 +43,7 @@ import foundation.e.apps.api.ecloud.EcloudRepository
import foundation.e.apps.api.fused.FusedAPIRepository
import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.api.gplay.utils.AC2DMTask
import foundation.e.apps.api.gplay.utils.AC2DMUtil
import foundation.e.apps.manager.database.fusedDownload.FusedDownload
import foundation.e.apps.manager.fused.FusedManagerRepository
import foundation.e.apps.manager.pkg.PkgManagerModule
@@ -51,6 +53,7 @@ import foundation.e.apps.utils.enums.Type
import foundation.e.apps.utils.enums.User
import foundation.e.apps.utils.enums.isInitialized
import foundation.e.apps.utils.enums.isUnFiltered
import foundation.e.apps.utils.modules.CommonUtilsModule.NETWORK_CODE_SUCCESS
import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis
import foundation.e.apps.utils.modules.DataStoreModule
import kotlinx.coroutines.Dispatchers
@@ -86,6 +89,12 @@ class MainActivityViewModel @Inject constructor(
    val purchaseDeclined: MutableLiveData<String> = MutableLiveData()
    var authRequestRunning = false

    /*
     * If this live data is populated, it means Google sign in failed.
     * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5709
     */
    val errorAuthResponse = MutableLiveData<PlayResponse>()

    /*
     * 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.
@@ -133,7 +142,19 @@ class MainActivityViewModel @Inject constructor(
    fun retryFetchingTokenAfterTimeout() {
        firstAuthDataFetchTime = 0
        setFirstTokenFetchTime()
        authValidity.postValue(false)
        if (isUserTypeGoogle()) {
            /*
             * Change done to show sign in error dialog for Google login.
             * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5709
             */
            if (authDataJson.value.isNullOrEmpty()) {
                generateAuthDataBasedOnUserType(User.GOOGLE.name)
            } else {
                validateAuthData()
            }
        } else {
            postFalseAuthValidity()
        }
    }

    fun uploadFaultyTokenToEcloud(description: String) {
@@ -163,7 +184,7 @@ class MainActivityViewModel @Inject constructor(
                 */
                if (!fusedAPIRepository.fetchAuthData()) {
                    authRequestRunning = false
                    authValidity.postValue(false)
                    postFalseAuthValidity()
                }
            }
        }
@@ -211,13 +232,33 @@ class MainActivityViewModel @Inject constructor(
    fun validateAuthData() {
        viewModelScope.launch {
            jsonToAuthData()?.let {
                val isAuthValid = isAuthValid(it)
                authValidity.postValue(isAuthValid)
                val validityResponse = getAuthValidityResponse(it)
                if (isUserTypeGoogle() && validityResponse.code != NETWORK_CODE_SUCCESS) {
                    /*
                     * Change done to show sign in error dialog for Google login.
                     * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5709
                     */
                    errorAuthResponse.postValue(validityResponse)
                } else {
                    authValidity.postValue(validityResponse.isSuccessful)
                }
                authRequestRunning = false
            }
        }
    }

    // Issue: https://gitlab.e.foundation/e/backlog/-/issues/5709
    fun isUserTypeGoogle(): Boolean {
        return userType.value == User.GOOGLE.name
    }

    /**
     * Useful to destroy credentials.
     */
    fun postFalseAuthValidity() {
        authValidity.postValue(false)
    }

    fun handleAuthDataJson() {
        val user = userType.value
        val json = authDataJson.value
@@ -291,7 +332,32 @@ class MainActivityViewModel @Inject constructor(
        var responseMap: Map<String, String>
        withContext(Dispatchers.IO) {
            val response = aC2DMTask.getAC2DMResponse(email, oauthToken)
            responseMap = response
            responseMap = if (response.isSuccessful) {
                AC2DMUtil.parseResponse(String(response.responseBytes))
            } else {
                mapOf()
            }
            if (isUserTypeGoogle() && response.code != NETWORK_CODE_SUCCESS) {
                /*
                 * For google login, the email and aasToken gets stored when
                 * we login through the webview, but that does not mean we have a valid authData.
                 *
                 * For first login, control flow is as below:
                 * In MainActivity, from userType observer -> handleAuthDataJson
                 * -> generateAuthDataBasedOnUserType -> doFetchAuthData -> this function
                 *
                 * If for first google login, google sign in portal was available
                 * but android.clients.google.com is unreachable, then responseMap is blank.
                 *
                 * We see validateAuthData is never called (which had a check for incorrect response)
                 * Hence we have to check the response code is NETWORK_CODE_SUCCESS (200) or not
                 * and show the Google sign in failed dialog.
                 *
                 * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5709
                 */
                errorAuthResponse.postValue(response)
                return@withContext
            }
            responseMap["Token"]?.let {
                if (fusedAPIRepository.fetchAuthData(email, it) == null) {
                    dataStoreModule.clearUserType()
@@ -313,7 +379,7 @@ class MainActivityViewModel @Inject constructor(
        }
    }

    private suspend fun isAuthValid(authData: AuthData): Boolean {
    private suspend fun getAuthValidityResponse(authData: AuthData): PlayResponse {
        return fusedAPIRepository.validateAuthData(authData)
    }

+2 −1
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.Category
import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.data.models.PlayResponse
import com.aurora.gplayapi.helpers.TopChartsHelper
import dagger.hilt.android.qualifiers.ApplicationContext
import foundation.e.apps.R
@@ -374,7 +375,7 @@ class FusedAPIImpl @Inject constructor(
        return gPlayAPIRepository.fetchAuthData(email, aasToken)
    }

    suspend fun validateAuthData(authData: AuthData): Boolean {
    suspend fun validateAuthData(authData: AuthData): PlayResponse {
        return gPlayAPIRepository.validateAuthData(authData)
    }

+6 −4
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.Category
import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.data.models.PlayResponse
import foundation.e.apps.api.ResultSupreme
import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.api.fused.data.FusedCategory
@@ -53,10 +54,11 @@ class FusedAPIRepository @Inject constructor(
        return fusedAPIImpl.getApplicationCategoryPreference()
    }

    suspend fun validateAuthData(authData: AuthData): Boolean {
        return authData.authToken.isNotEmpty() && authData.deviceInfoProvider != null && fusedAPIImpl.validateAuthData(
            authData
        )
    suspend fun validateAuthData(authData: AuthData): PlayResponse {
        if (authData.authToken.isNotEmpty() && authData.deviceInfoProvider != null) {
            return fusedAPIImpl.validateAuthData(authData)
        }
        return PlayResponse()
    }

    suspend fun getApplicationDetails(
+8 −7
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import com.aurora.gplayapi.data.models.File
import com.aurora.gplayapi.data.models.SearchBundle
import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.data.models.PlayResponse
import com.aurora.gplayapi.helpers.AppDetailsHelper
import com.aurora.gplayapi.helpers.AuthValidator
import com.aurora.gplayapi.helpers.CategoryHelper
@@ -39,6 +40,7 @@ import com.aurora.gplayapi.helpers.StreamHelper
import com.aurora.gplayapi.helpers.TopChartsHelper
import dagger.hilt.android.qualifiers.ApplicationContext
import foundation.e.apps.api.gplay.token.TokenRepository
import foundation.e.apps.api.gplay.utils.CustomAuthValidator
import foundation.e.apps.api.gplay.utils.GPlayHttpClient
import foundation.e.apps.utils.modules.DataStoreModule
import kotlinx.coroutines.Dispatchers
@@ -83,19 +85,18 @@ class GPlayAPIImpl @Inject constructor(
        return null
    }

    suspend fun validateAuthData(authData: AuthData): Boolean {
        var validity: Boolean
    suspend fun validateAuthData(authData: AuthData): PlayResponse {
        var result = PlayResponse()
        withContext(Dispatchers.IO) {
            validity = try {
                val authValidator = AuthValidator(authData).using(gPlayHttpClient)
                authValidator.isValid()
            try {
                val authValidator = CustomAuthValidator(authData).using(gPlayHttpClient)
                result = authValidator.getValidityResponse()
            } catch (e: Exception) {
                e.printStackTrace()
                throw e
                false
            }
        }
        return validity
        return result
    }

    suspend fun getSearchSuggestions(query: String, authData: AuthData): List<SearchSuggestEntry> {
Loading