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

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

MR 14: Remove old code

parent 9f51cd6c
Loading
Loading
Loading
Loading
+0 −14
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ 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.distinctUntilChanged
@@ -337,19 +336,6 @@ 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)
+3 −288
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.os.Build
import android.os.SystemClock
import android.util.Base64
import android.widget.ImageView
import androidx.annotation.RequiresApi
@@ -35,40 +34,31 @@ 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
import foundation.e.apps.api.cleanapk.blockedApps.BlockedAppRepository
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
import foundation.e.apps.utils.Constants.timeoutDurationInMillis
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.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.DataStoreModule
import foundation.e.apps.utils.modules.PWAManagerModule
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ru.beryukhov.reactivenetwork.ReactiveNetwork
import timber.log.Timber
import java.io.ByteArrayOutputStream
import javax.inject.Inject

@HiltViewModel
class MainActivityViewModel @Inject constructor(
    private val gson: Gson,
    private val dataStoreModule: DataStoreModule,
    private val fusedAPIRepository: FusedAPIRepository,
    private val fusedManagerRepository: FusedManagerRepository,
@@ -76,40 +66,17 @@ class MainActivityViewModel @Inject constructor(
    private val pwaManagerModule: PWAManagerModule,
    private val ecloudRepository: EcloudRepository,
    private val blockedAppRepository: BlockedAppRepository,
    private val aC2DMTask: AC2DMTask,
) : ViewModel() {

    val authDataJson: LiveData<String> = dataStoreModule.authData.asLiveData()
    val tocStatus: LiveData<Boolean> = dataStoreModule.tocStatus.asLiveData()
    val userType: LiveData<String> = dataStoreModule.userType.asLiveData()

    private var _authData: MutableLiveData<AuthData> = MutableLiveData()
    val authData: LiveData<AuthData> = _authData
    val authValidity: MutableLiveData<Boolean> = MutableLiveData()
    private val _purchaseAppLiveData: MutableLiveData<FusedDownload> = MutableLiveData()
    val purchaseAppLiveData: LiveData<FusedDownload> = _purchaseAppLiveData
    val isAppPurchased: MutableLiveData<String> = MutableLiveData()
    val purchaseDeclined: MutableLiveData<String> = MutableLiveData()
    var authRequestRunning = false

    var gPlayAuthData = AuthData("", "")

    /*
     * 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.
     *
     * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404
     */
    var firstAuthDataFetchTime = 0L

    var isTokenValidationCompletedOnce = false

    // Downloads
    val downloadList = fusedManagerRepository.getDownloadLiveList()
    var installInProgress = false
@@ -118,9 +85,6 @@ class MainActivityViewModel @Inject constructor(

    private val _errorMessageStringResource = MutableLiveData<Int>()
    val errorMessageStringResource: LiveData<Int> = _errorMessageStringResource
    /*
     * Authentication related functions
     */

    companion object {
        private const val TAG = "MainActivityViewModel"
@@ -135,261 +99,12 @@ class MainActivityViewModel @Inject constructor(
        return dataStoreModule.getEmail()
    }

    private fun setFirstTokenFetchTime() {
        if (firstAuthDataFetchTime == 0L) {
            firstAuthDataFetchTime = SystemClock.uptimeMillis()
        }
    }

    private 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() {
        firstAuthDataFetchTime = 0
        setFirstTokenFetchTime()
        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(email: String, description: String = "") {
        viewModelScope.launch {
            ecloudRepository.uploadFaultyEmail(email, description)
        }
    }

    fun getAuthData() {
        if (!authRequestRunning) {
            authRequestRunning = true
            viewModelScope.launch {
                /*
                 * If getting auth data failed, try getting again.
                 * Sending false in authValidity, triggers observer in MainActivity,
                 * causing it to destroy credentials and try to regenerate auth data.
                 *
                 * Issue:
                 * https://gitlab.e.foundation/e/backlog/-/issues/5413
                 * https://gitlab.e.foundation/e/backlog/-/issues/5404
                 */
                if (!fusedAPIRepository.fetchAuthData()) {
                    authRequestRunning = false
                    postFalseAuthValidity()
                }
            }
        }
    }

    fun updateAuthData(authData: AuthData) {
        _authData.value = authData
    }

    fun destroyCredentials(regenerateFunction: ((user: String) -> Unit)?) {
        viewModelScope.launch {
            /*
             * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5168
             *
             * Now destroyCredentials() no longer removes the user type from data store.
             * (i.e. Google login or Anonymous).
             * - If the type is User.ANONYMOUS then we do not prompt the user to login again,
             *   we directly generate new auth data; which is the main Gitlab issue described above.
             * - If not anonymous user, i.e. type is User.GOOGLE, in that case we clear
             *   the USERTYPE value. This causes HomeFragment.onTosAccepted() to open
             *   SignInFragment as we need fresh login from the user.
             */
            dataStoreModule.destroyCredentials()
            if (regenerateFunction != null) {
                dataStoreModule.userType.collect { user ->
                    if (!user.isBlank() && User.valueOf(user) == User.ANONYMOUS) {
                        Timber.d("Regenerating auth data for Anonymous user")
                        regenerateFunction(user)
                    } else {
                        Timber.d("Ask Google user to log in again")
                        dataStoreModule.clearUserType()
                    }
                }
            }
        }
    }

    fun generateAuthData() {
        val data = jsonToAuthData()
        _authData.value = data
    }

    private fun jsonToAuthData() = gson.fromJson(authDataJson.value, AuthData::class.java)

    fun validateAuthData() {
        viewModelScope.launch {
            jsonToAuthData()?.let {
                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

        if (user == null || json == null) {
            return
        }
        Timber.d(">>> handleAuthDataJson: internet: ${internetConnection.value}")
        if (!isUserLoggedIn(user, json)) {
            generateAuthDataBasedOnUserType(user)
        } else if (isEligibleToValidateJson(json) && internetConnection.value == true) {
            validateAuthData()
            Timber.d(">>> Authentication data is available!")
        }
    }

    private fun isUserLoggedIn(user: String, json: String) =
        user.isNotEmpty() && !user.contentEquals(User.UNAVAILABLE.name) && json.isNotEmpty()

    private fun isEligibleToValidateJson(authDataJson: String?) =
        !authDataJson.isNullOrEmpty() && !userType.value.isNullOrEmpty() && !userType.value.contentEquals(
            User.UNAVAILABLE.name
        ) && authValidity.value != true

    fun handleAuthValidity(isValid: Boolean, handleTimeoOut: () -> Unit) {
        if (isGoogleLoginRunning) {
            return
        }
        isTokenValidationCompletedOnce = true
        if (isValid) {
            Timber.d("Authentication data is valid!")
            generateAuthData()
            return
        }
        Timber.d(">>> Authentication data validation failed!")
        destroyCredentials { user ->
            if (isTimeEligibleForTokenRefresh()) {
                generateAuthDataBasedOnUserType(user)
            } else {
                handleTimeoOut()
            }
        }
    }

    private fun generateAuthDataBasedOnUserType(user: String) {
        if (user.isEmpty() || tocStatus.value == false || isGoogleLoginRunning) {
            return
        }
        when (User.valueOf(user)) {
            User.ANONYMOUS -> {
                if (authDataJson.value.isNullOrEmpty() && !authRequestRunning) {
                    Timber.d(">>> Fetching new authentication data")
                    setFirstTokenFetchTime()
                    getAuthData()
                }
            }
            User.UNAVAILABLE -> {
                destroyCredentials(null)
            }
            User.GOOGLE -> {
                if (authData.value == null && !authRequestRunning) {
                    Timber.d(">>> Fetching new authentication data")
                    setFirstTokenFetchTime()
                    doFetchAuthData()
                }
            }
        }
    }

    private suspend fun doFetchAuthData(email: String, oauthToken: String) {
        var responseMap: Map<String, String>
        withContext(Dispatchers.IO) {
            val response = aC2DMTask.getAC2DMResponse(email, oauthToken)
            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()
                    _errorMessageStringResource.value = R.string.unknown_error
                }
            }
        }
    }

    private fun doFetchAuthData() {
        viewModelScope.launch {
            isGoogleLoginRunning = true
            val email = dataStoreModule.getEmail()
            val oauthToken = dataStoreModule.getAASToken()
            if (email.isNotEmpty() && oauthToken.isNotEmpty()) {
                doFetchAuthData(email, oauthToken)
            }
            isGoogleLoginRunning = false
        }
    }

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

    /*
     * Notification functions
     */
@@ -416,7 +131,7 @@ class MainActivityViewModel @Inject constructor(
     * Issue: https://gitlab.e.foundation/e/os/backlog/-/issues/266
     */
    fun shouldShowPaidAppsSnackBar(app: FusedApp): Boolean {
        if (!app.isFree && authData.value?.isAnonymous == true) {
        if (!app.isFree && gPlayAuthData.isAnonymous) {
            _errorMessageStringResource.value = R.string.paid_app_anonymous_message
            return true
        }
@@ -553,7 +268,7 @@ class MainActivityViewModel @Inject constructor(

    suspend fun updateAwaitingForPurchasedApp(packageName: String): FusedDownload? {
        val fusedDownload = fusedManagerRepository.getFusedDownload(packageName = packageName)
        authData.value?.let {
        gPlayAuthData.let {
            if (!it.isAnonymous) {
                try {
                    fusedAPIRepository.updateFusedDownloadWithDownloadingInfo(
@@ -594,7 +309,7 @@ class MainActivityViewModel @Inject constructor(
        fusedDownload: FusedDownload
    ) {
        val downloadList = mutableListOf<String>()
        authData.value?.let {
        gPlayAuthData.let {
            if (app.type == Type.PWA) {
                downloadList.add(app.url)
                fusedDownload.downloadURLList = downloadList
+0 −12
Original line number Diff line number Diff line
@@ -465,18 +465,6 @@ class FusedAPIImpl @Inject constructor(
        return gPlayAPIRepository.getSearchSuggestions(query, authData)
    }

    suspend fun fetchAuthData(): Boolean {
        return gPlayAPIRepository.fetchAuthData()
    }

    suspend fun fetchAuthData(email: String, aasToken: String): AuthData? {
        return gPlayAPIRepository.fetchAuthData(email, aasToken)
    }

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

    suspend fun getOnDemandModule(
        authData: AuthData,
        packageName: String,
+1 −19
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.Category
import com.aurora.gplayapi.data.models.PlayResponse
import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
import foundation.e.apps.api.ResultSupreme
@@ -39,9 +38,7 @@ import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class FusedAPIRepository @Inject constructor(
    private val fusedAPIImpl: FusedAPIImpl
) {
class FusedAPIRepository @Inject constructor(private val fusedAPIImpl: FusedAPIImpl) {

    var streamBundle = StreamBundle()
        private set
@@ -93,13 +90,6 @@ class FusedAPIRepository @Inject constructor(
        return fusedAPIImpl.getApplicationCategoryPreference()
    }

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

    suspend fun getApplicationDetails(
        packageNameList: List<String>,
        authData: AuthData,
@@ -165,14 +155,6 @@ class FusedAPIRepository @Inject constructor(
        return fusedAPIImpl.getSearchSuggestions(query, authData)
    }

    suspend fun fetchAuthData(): Boolean {
        return fusedAPIImpl.fetchAuthData()
    }

    suspend fun fetchAuthData(email: String, aasToken: String): AuthData? {
        return fusedAPIImpl.fetchAuthData(email, aasToken)
    }

    fun getSearchResults(
        query: String,
        authData: AuthData
+1 −55
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.Category
import com.aurora.gplayapi.data.models.File
import com.aurora.gplayapi.data.models.PlayResponse
import com.aurora.gplayapi.data.models.SearchBundle
import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
@@ -37,66 +36,13 @@ import com.aurora.gplayapi.helpers.PurchaseHelper
import com.aurora.gplayapi.helpers.SearchHelper
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
import kotlinx.coroutines.async
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext
import javax.inject.Inject

class GPlayAPIImpl @Inject constructor(
    @ApplicationContext private val context: Context,
    private val tokenRepository: TokenRepository,
    private val dataStoreModule: DataStoreModule,
    private val gPlayHttpClient: GPlayHttpClient
) {

    /**
     * Save auth data to preferences.
     * Updated for network failures.
     * Issue:
     * https://gitlab.e.foundation/e/backlog/-/issues/5413
     * https://gitlab.e.foundation/e/backlog/-/issues/5404
     *
     * @return true or false based on if the request was successful.
     */
    // TODO: DON'T HARDCODE DISPATCHERS IN ANY METHODS
    suspend fun fetchAuthData(): Boolean = withContext(Dispatchers.IO) {
        val data = async { tokenRepository.getAuthData() }
        data.await().let {
            if (it == null) return@withContext false
            it.locale = context.resources.configuration.locales[0] // update locale with the default locale from settings
            dataStoreModule.saveCredentials(it)
            return@withContext true
        }
    }

    suspend fun fetchAuthData(email: String, aasToken: String): AuthData? {
        val authData = tokenRepository.getAuthData(email, aasToken)
        if (authData.authToken.isNotEmpty() && authData.deviceInfoProvider != null) {
            dataStoreModule.saveCredentials(authData)
            return authData
        }
        return null
    }

    suspend fun validateAuthData(authData: AuthData): PlayResponse {
        var result = PlayResponse()
        withContext(Dispatchers.IO) {
            try {
                val authValidator = CustomAuthValidator(authData).using(gPlayHttpClient)
                result = authValidator.getValidityResponse()
            } catch (e: Exception) {
                e.printStackTrace()
                throw e
            }
        }
        return result
    }
class GPlayAPIImpl @Inject constructor(private val gPlayHttpClient: GPlayHttpClient) {

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