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

Commit edc7447d authored by Jonathan Klee's avatar Jonathan Klee
Browse files

Revert "Merge branch '1111-login-rewrite-full' into 'main'"

This reverts commit e898a2dc, reversing
changes made to f6de015c.
parent 95c5ee72
Loading
Loading
Loading
Loading
Loading
+42 −34
Original line number Original line Diff line number Diff line
@@ -25,9 +25,9 @@ import android.os.Environment
import android.os.StatFs
import android.os.StatFs
import android.os.storage.StorageManager
import android.os.storage.StorageManager
import android.view.View
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.NavController
import androidx.navigation.NavOptions
import androidx.navigation.NavOptions
@@ -35,15 +35,12 @@ import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.NavigationUI
import androidx.navigation.ui.NavigationUI
import androidx.navigation.ui.setupWithNavController
import androidx.navigation.ui.setupWithNavController
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.exceptions.ApiException
import com.aurora.gplayapi.exceptions.ApiException
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.AndroidEntryPoint
import foundation.e.apps.application.subFrags.ApplicationDialogFragment
import foundation.e.apps.application.subFrags.ApplicationDialogFragment
import foundation.e.apps.databinding.ActivityMainBinding
import foundation.e.apps.databinding.ActivityMainBinding
import foundation.e.apps.login.AuthObject
import foundation.e.apps.login.LoginViewModel
import foundation.e.apps.manager.database.fusedDownload.FusedDownload
import foundation.e.apps.manager.database.fusedDownload.FusedDownload
import foundation.e.apps.manager.workmanager.InstallWorkManager
import foundation.e.apps.manager.workmanager.InstallWorkManager
import foundation.e.apps.purchase.AppPurchaseFragmentDirections
import foundation.e.apps.purchase.AppPurchaseFragmentDirections
@@ -53,12 +50,11 @@ import foundation.e.apps.updates.UpdatesNotifier
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.eventBus.AppEvent
import foundation.e.apps.utils.eventBus.AppEvent
import foundation.e.apps.utils.eventBus.EventBus
import foundation.e.apps.utils.eventBus.EventBus
import foundation.e.apps.utils.exceptions.GPlayValidationException
import foundation.e.apps.utils.modules.CommonUtilsModule
import foundation.e.apps.utils.modules.CommonUtilsModule
import foundation.e.apps.utils.parentFragment.TimeoutFragment
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import kotlinx.coroutines.launch
import org.json.JSONObject
import timber.log.Timber
import timber.log.Timber
import java.io.File
import java.io.File
import java.util.UUID
import java.util.UUID
@@ -66,7 +62,6 @@ import java.util.UUID
@AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
class MainActivity : AppCompatActivity() {
    private lateinit var signInViewModel: SignInViewModel
    private lateinit var signInViewModel: SignInViewModel
    private lateinit var loginViewModel: LoginViewModel
    private lateinit var binding: ActivityMainBinding
    private lateinit var binding: ActivityMainBinding
    private val TAG = MainActivity::class.java.simpleName
    private val TAG = MainActivity::class.java.simpleName
    private lateinit var viewModel: MainActivityViewModel
    private lateinit var viewModel: MainActivityViewModel
@@ -88,7 +83,6 @@ class MainActivity : AppCompatActivity() {


        viewModel = ViewModelProvider(this)[MainActivityViewModel::class.java]
        viewModel = ViewModelProvider(this)[MainActivityViewModel::class.java]
        signInViewModel = ViewModelProvider(this)[SignInViewModel::class.java]
        signInViewModel = ViewModelProvider(this)[SignInViewModel::class.java]
        loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]


        // navOptions and activityNavController for TOS and SignIn Fragments
        // navOptions and activityNavController for TOS and SignIn Fragments
        val navOptions = NavOptions.Builder()
        val navOptions = NavOptions.Builder()
@@ -96,11 +90,9 @@ class MainActivity : AppCompatActivity() {
            .build()
            .build()
        navOptions.shouldLaunchSingleTop()
        navOptions.shouldLaunchSingleTop()


        viewModel.tocStatus.distinctUntilChanged().observe(this) {
        viewModel.tocStatus.observe(this) {
            if (it != true) {
            if (it != true) {
                navController.navigate(R.id.TOSFragment, null, navOptions)
                navController.navigate(R.id.TOSFragment, null, navOptions)
            } else {
                loginViewModel.startLoginFlow()
            }
            }
        }
        }


@@ -109,34 +101,37 @@ class MainActivity : AppCompatActivity() {
            if (isInternetAvailable) {
            if (isInternetAvailable) {
                binding.noInternet.visibility = View.GONE
                binding.noInternet.visibility = View.GONE
                binding.fragment.visibility = View.VISIBLE
                binding.fragment.visibility = View.VISIBLE

                // Watch and refresh authentication data
                if (viewModel.authDataJson.value == null) {
                    viewModel.authDataJson.observe(this) {
                        viewModel.handleAuthDataJson()
                    }
                }
                }
            }
            }
        }

        viewModel.userType.observe(this) { user ->
            viewModel.handleAuthDataJson()
        }


        loginViewModel.authObjects.distinctUntilChanged().observe(this) {
        if (signInViewModel.authLiveData.value == null) {
            when {
            signInViewModel.authLiveData.observe(this) {
                it == null -> return@observe
                viewModel.updateAuthData(it)
                it.isEmpty() -> {
                    // No auth type defined means user has not logged in yet
                    // Pop back stack to prevent showing TOSFragment on pressing back button.
                    navController.popBackStack()
                    navController.navigate(R.id.signInFragment)
            }
            }
                else -> {}
        }
        }


            it.find { it is AuthObject.GPlayAuth }?.result?.run {
        viewModel.errorAuthResponse.observe(this) {
                if (isSuccess()) {
            onSignInError()
                    viewModel.gPlayAuthData = data as AuthData
                } else if (exception is GPlayValidationException) {
                    val email = otherPayload.toString()
                    val descriptionJson = JSONObject().apply {
                        put("versionName", BuildConfig.VERSION_NAME)
                        put("versionCode", BuildConfig.VERSION_CODE)
                        put("debuggable", BuildConfig.DEBUG)
                        put("device", Build.DEVICE)
                        put("api", Build.VERSION.SDK_INT)
        }
        }
                    viewModel.uploadFaultyTokenToEcloud(email, descriptionJson.toString())

        viewModel.authValidity.observe(this) {
            viewModel.handleAuthValidity(it) {
                Timber.d("Timeout validating auth data!")
                val lastFragment = navHostFragment.childFragmentManager.fragments[0]
                if (lastFragment is TimeoutFragment) {
                    Timber.d("Displaying timeout from MainActivity on fragment: " + lastFragment.javaClass.name)
                    lastFragment.onTimeout()
                }
                }
            }
            }
        }
        }
@@ -336,6 +331,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 {
    private fun getAvailableInternalMemorySize(): Long {
        val path: File = Environment.getDataDirectory()
        val path: File = Environment.getDataDirectory()
        val stat = StatFs(path.path)
        val stat = StatFs(path.path)
+295 −10
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.Context
import android.content.Intent
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Bitmap
import android.os.Build
import android.os.Build
import android.os.SystemClock
import android.util.Base64
import android.util.Base64
import android.widget.ImageView
import android.widget.ImageView
import androidx.annotation.RequiresApi
import androidx.annotation.RequiresApi
@@ -34,12 +35,16 @@ import androidx.lifecycle.asLiveData
import androidx.lifecycle.liveData
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewModelScope
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.PlayResponse
import com.aurora.gplayapi.exceptions.ApiException
import com.aurora.gplayapi.exceptions.ApiException
import com.google.gson.Gson
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import foundation.e.apps.api.cleanapk.blockedApps.BlockedAppRepository
import foundation.e.apps.api.cleanapk.blockedApps.BlockedAppRepository
import foundation.e.apps.api.ecloud.EcloudRepository
import foundation.e.apps.api.ecloud.EcloudRepository
import foundation.e.apps.api.fused.FusedAPIRepository
import foundation.e.apps.api.fused.FusedAPIRepository
import foundation.e.apps.api.fused.data.FusedApp
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.database.fusedDownload.FusedDownload
import foundation.e.apps.manager.fused.FusedManagerRepository
import foundation.e.apps.manager.fused.FusedManagerRepository
import foundation.e.apps.manager.pkg.PkgManagerModule
import foundation.e.apps.manager.pkg.PkgManagerModule
@@ -49,16 +54,21 @@ import foundation.e.apps.utils.enums.Type
import foundation.e.apps.utils.enums.User
import foundation.e.apps.utils.enums.User
import foundation.e.apps.utils.enums.isInitialized
import foundation.e.apps.utils.enums.isInitialized
import foundation.e.apps.utils.enums.isUnFiltered
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 foundation.e.apps.utils.modules.DataStoreModule
import foundation.e.apps.utils.modules.PWAManagerModule
import foundation.e.apps.utils.modules.PWAManagerModule
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ru.beryukhov.reactivenetwork.ReactiveNetwork
import ru.beryukhov.reactivenetwork.ReactiveNetwork
import timber.log.Timber
import java.io.ByteArrayOutputStream
import java.io.ByteArrayOutputStream
import javax.inject.Inject
import javax.inject.Inject


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


    val authDataJson: LiveData<String> = dataStoreModule.authData.asLiveData()
    val tocStatus: LiveData<Boolean> = dataStoreModule.tocStatus.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()
    private val _purchaseAppLiveData: MutableLiveData<FusedDownload> = MutableLiveData()
    val purchaseAppLiveData: LiveData<FusedDownload> = _purchaseAppLiveData
    val purchaseAppLiveData: LiveData<FusedDownload> = _purchaseAppLiveData
    val isAppPurchased: MutableLiveData<String> = MutableLiveData()
    val isAppPurchased: MutableLiveData<String> = MutableLiveData()
    val purchaseDeclined: 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
    // Downloads
    val downloadList = fusedManagerRepository.getDownloadLiveList()
    val downloadList = fusedManagerRepository.getDownloadLiveList()
@@ -85,6 +116,9 @@ class MainActivityViewModel @Inject constructor(


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


    companion object {
    companion object {
        private const val TAG = "MainActivityViewModel"
        private const val TAG = "MainActivityViewModel"
@@ -92,18 +126,269 @@ class MainActivityViewModel @Inject constructor(
    }
    }


    fun getUser(): User {
    fun getUser(): User {
        return dataStoreModule.getUserType()
        return User.valueOf(userType.value ?: User.UNAVAILABLE.name)
    }

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


    fun getUserEmail(): String {
    private fun isTimeEligibleForTokenRefresh(): Boolean {
        return dataStoreModule.getEmail()
        return (SystemClock.uptimeMillis() - firstAuthDataFetchTime) <= timeoutDurationInMillis
    }
    }


    fun uploadFaultyTokenToEcloud(email: String, description: String = "") {
    /*
     * 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(description: String) {
        viewModelScope.launch {
        viewModelScope.launch {
            authData.value?.let { authData ->
                val email: String = authData.run {
                    if (email != "null") email
                    else userProfile?.email ?: "null"
                }
                ecloudRepository.uploadFaultyEmail(email, description)
                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
     * Notification functions
@@ -131,7 +416,7 @@ class MainActivityViewModel @Inject constructor(
     * Issue: https://gitlab.e.foundation/e/os/backlog/-/issues/266
     * Issue: https://gitlab.e.foundation/e/os/backlog/-/issues/266
     */
     */
    fun shouldShowPaidAppsSnackBar(app: FusedApp): Boolean {
    fun shouldShowPaidAppsSnackBar(app: FusedApp): Boolean {
        if (!app.isFree && gPlayAuthData.isAnonymous) {
        if (!app.isFree && authData.value?.isAnonymous == true) {
            _errorMessageStringResource.value = R.string.paid_app_anonymous_message
            _errorMessageStringResource.value = R.string.paid_app_anonymous_message
            return true
            return true
        }
        }
@@ -175,7 +460,7 @@ class MainActivityViewModel @Inject constructor(
     */
     */
    fun verifyUiFilter(fusedApp: FusedApp, method: () -> Unit) {
    fun verifyUiFilter(fusedApp: FusedApp, method: () -> Unit) {
        viewModelScope.launch {
        viewModelScope.launch {
            val authData = gPlayAuthData
            val authData = authData.value
            if (fusedApp.filterLevel.isInitialized()) {
            if (fusedApp.filterLevel.isInitialized()) {
                method()
                method()
            } else {
            } else {
@@ -268,7 +553,7 @@ class MainActivityViewModel @Inject constructor(


    suspend fun updateAwaitingForPurchasedApp(packageName: String): FusedDownload? {
    suspend fun updateAwaitingForPurchasedApp(packageName: String): FusedDownload? {
        val fusedDownload = fusedManagerRepository.getFusedDownload(packageName = packageName)
        val fusedDownload = fusedManagerRepository.getFusedDownload(packageName = packageName)
        gPlayAuthData.let {
        authData.value?.let {
            if (!it.isAnonymous) {
            if (!it.isAnonymous) {
                try {
                try {
                    fusedAPIRepository.updateFusedDownloadWithDownloadingInfo(
                    fusedAPIRepository.updateFusedDownloadWithDownloadingInfo(
@@ -309,7 +594,7 @@ class MainActivityViewModel @Inject constructor(
        fusedDownload: FusedDownload
        fusedDownload: FusedDownload
    ) {
    ) {
        val downloadList = mutableListOf<String>()
        val downloadList = mutableListOf<String>()
        gPlayAuthData.let {
        authData.value?.let {
            if (app.type == Type.PWA) {
            if (app.type == Type.PWA) {
                downloadList.add(app.url)
                downloadList.add(app.url)
                fusedDownload.downloadURLList = downloadList
                fusedDownload.downloadURLList = downloadList
+3 −8
Original line number Original line Diff line number Diff line
@@ -70,7 +70,7 @@ sealed class ResultSupreme<T> {
         * @param message A String message to log or display to the user.
         * @param message A String message to log or display to the user.
         * @param exception Optional exception from try-catch block.
         * @param exception Optional exception from try-catch block.
         */
         */
        constructor(message: String, exception: Exception? = null) : this() {
        constructor(message: String, exception: Exception = Exception()) : this() {
            this.message = message
            this.message = message
            this.exception = exception
            this.exception = exception
        }
        }
@@ -91,11 +91,6 @@ sealed class ResultSupreme<T> {
    var data: T? = null
    var data: T? = null
        private set
        private set


    /**
     * Any other information that needs to be transmitted.
     */
    var otherPayload: Any? = null

    /**
    /**
     * A custom string message for logging or displaying to the user.
     * A custom string message for logging or displaying to the user.
     */
     */
@@ -104,7 +99,7 @@ sealed class ResultSupreme<T> {
    /**
    /**
     * Exception from try-catch block for error cases.
     * Exception from try-catch block for error cases.
     */
     */
    var exception: Exception? = null
    var exception: Exception = Exception()


    fun isValidData() = data != null
    fun isValidData() = data != null


@@ -126,7 +121,7 @@ sealed class ResultSupreme<T> {
            status: ResultStatus,
            status: ResultStatus,
            data: T? = null,
            data: T? = null,
            message: String = "",
            message: String = "",
            exception: Exception? = null,
            exception: Exception = Exception(),
        ): ResultSupreme<T> {
        ): ResultSupreme<T> {
            val resultObject = when {
            val resultObject = when {
                status == ResultStatus.OK && data != null -> Success<T>(data)
                status == ResultStatus.OK && data != null -> Success<T>(data)
+15 −2

File changed.

Preview size limit exceeded, changes collapsed.

+19 −1
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@ import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.Category
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.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.data.models.StreamCluster
import foundation.e.apps.api.ResultSupreme
import foundation.e.apps.api.ResultSupreme
@@ -38,7 +39,9 @@ import javax.inject.Inject
import javax.inject.Singleton
import javax.inject.Singleton


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


    var streamBundle = StreamBundle()
    var streamBundle = StreamBundle()
        private set
        private set
@@ -90,6 +93,13 @@ class FusedAPIRepository @Inject constructor(private val fusedAPIImpl: FusedAPII
        return fusedAPIImpl.getApplicationCategoryPreference()
        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(
    suspend fun getApplicationDetails(
        packageNameList: List<String>,
        packageNameList: List<String>,
        authData: AuthData,
        authData: AuthData,
@@ -155,6 +165,14 @@ class FusedAPIRepository @Inject constructor(private val fusedAPIImpl: FusedAPII
        return fusedAPIImpl.getSearchSuggestions(query, authData)
        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(
    fun getSearchResults(
        query: String,
        query: String,
        authData: AuthData
        authData: AuthData
Loading