diff --git a/app/src/main/java/foundation/e/apps/data/login/AuthDataValidator.kt b/app/src/main/java/foundation/e/apps/data/login/AuthDataValidator.kt
deleted file mode 100644
index 908fe70b5fde7e63ef2f2e33bd6ba495b7c0b109..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/AuthDataValidator.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright MURENA SAS 2023
- * Apps Quickly and easily install Android apps onto your device!
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package foundation.e.apps.data.login
-
-import com.aurora.gplayapi.data.models.AuthData
-import foundation.e.apps.data.ResultSupreme
-
-interface AuthDataValidator {
- suspend fun validateAuthData(): ResultSupreme
-}
diff --git a/app/src/main/java/foundation/e/apps/data/login/AuthenticatorRepository.kt b/app/src/main/java/foundation/e/apps/data/login/AuthenticatorRepository.kt
deleted file mode 100644
index 6c98a3801d495ee7cefa86099333c3cd2ab8231f..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/AuthenticatorRepository.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2019-2022 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package foundation.e.apps.data.login
-
-import com.aurora.gplayapi.data.models.AuthData
-import foundation.e.apps.data.ResultSupreme
-import foundation.e.apps.data.enums.User
-import foundation.e.apps.data.login.exceptions.GPlayLoginException
-import foundation.e.apps.data.preference.AppLoungeDataStore
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@JvmSuppressWildcards
-@Singleton
-class AuthenticatorRepository @Inject constructor(
- private val loginCommon: LoginCommon,
- private val authenticators: List,
- private val appLoungeDataStore: AppLoungeDataStore
-) {
-
- fun getGPlayAuthOrThrow(): AuthData {
- return kotlin.runCatching {
- appLoungeDataStore.getAuthData()
- }.getOrElse {
- throw GPlayLoginException(false, "AuthData is not available", appLoungeDataStore.getUser())
- }
- }
-
- suspend fun setGPlayAuth(auth: AuthData) {
- appLoungeDataStore.saveAuthData(auth)
- }
-
- suspend fun fetchAuthObjects(authTypes: List = listOf()): List {
-
- val authObjectsLocal = ArrayList()
-
- for (authenticator in authenticators) {
- if (!authenticator.isStoreActive()) continue
- if (authenticator::class.java.simpleName in authTypes) {
- authenticator.logout()
- }
-
- val authObject = authenticator.login()
- authObjectsLocal.add(authObject)
-
- if (authObject is AuthObject.GPlayAuth) {
- appLoungeDataStore.saveAuthData(authObject.result.data)
- }
- }
-
- return authObjectsLocal
- }
-
- suspend fun saveUserType(user: User) {
- loginCommon.saveUserType(user)
- }
-
- suspend fun saveGoogleLogin(email: String, oauth: String) {
- loginCommon.saveGoogleLogin(email, oauth)
- }
-
- suspend fun setNoGoogleMode() {
- loginCommon.setNoGoogleMode()
- }
-
- suspend fun logout() {
- loginCommon.logout()
- }
-
- suspend fun getValidatedAuthData(): ResultSupreme {
- val authDataValidator = (authenticators.find { it is AuthDataValidator } as AuthDataValidator)
- val validateAuthData = authDataValidator.validateAuthData()
- appLoungeDataStore.saveAuthData(validateAuthData.data)
- return validateAuthData
- }
-
- private fun getUserType(): User {
- return loginCommon.getUserType()
- }
-}
diff --git a/app/src/main/java/foundation/e/apps/data/login/CleanApkAuthenticator.kt b/app/src/main/java/foundation/e/apps/data/login/CleanApkAuthenticator.kt
deleted file mode 100644
index 8dac6ac1aa04b04be7c7cb0e75d810efb16f8e71..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/CleanApkAuthenticator.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2019-2022 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package foundation.e.apps.data.login
-
-import foundation.e.apps.data.ResultSupreme
-import foundation.e.apps.data.enums.User
-import foundation.e.apps.data.preference.AppLoungeDataStore
-import foundation.e.apps.data.preference.AppLoungePreference
-import javax.inject.Inject
-import javax.inject.Singleton
-
-/**
- * Just a dummy class for CleanApk, as it requires no authentication.
- * https://gitlab.e.foundation/e/backlog/-/issues/5680
- */
-@Singleton
-class CleanApkAuthenticator @Inject constructor(
- private val appLoungeDataStore: AppLoungeDataStore,
- private val appLoungePreference: AppLoungePreference,
-) : StoreAuthenticator {
-
- private val user: User
- get() = appLoungeDataStore.getUser()
-
- override fun isStoreActive(): Boolean {
- if (user == User.UNAVAILABLE) {
- /*
- * UNAVAILABLE user means first login is not completed.
- */
- return false
- }
- return appLoungePreference.isOpenSourceSelected() || appLoungePreference.isPWASelected()
- }
-
- override suspend fun login(): AuthObject.CleanApk {
- return AuthObject.CleanApk(
- ResultSupreme.Success(Unit),
- user,
- )
- }
-
- override suspend fun logout() {}
-}
diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt b/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt
deleted file mode 100644
index f2ef1fe12ab151638e74fa856d4d185d217492b1..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2019-2025 e Foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- */
-
-package foundation.e.apps.data.login
-
-import foundation.e.apps.data.enums.User
-import foundation.e.apps.data.preference.AppLoungeDataStore
-import foundation.e.apps.data.preference.AppLoungePreference
-import javax.inject.Inject
-import javax.inject.Singleton
-
-/**
- * Contains common function for first login, logout, get which type of authentication / source
- * to be used etc...
- *
- * https://gitlab.e.foundation/e/backlog/-/issues/5680
- */
-@Singleton
-class LoginCommon @Inject constructor(
- private val appLoungeDataStore: AppLoungeDataStore,
- private val appLoungePreference: AppLoungePreference,
-) {
- suspend fun saveUserType(user: User) {
- appLoungeDataStore.saveUserType(user)
- }
-
- fun getUserType(): User {
- return appLoungeDataStore.getUser()
- }
-
- suspend fun saveGoogleLogin(email: String, oauth: String) {
- appLoungeDataStore.saveGoogleLogin(email, oauth)
- }
-
- suspend fun setNoGoogleMode() {
- appLoungePreference.run {
- disablePlayStore()
- enableOpenSource()
- enablePwa()
- }
- appLoungeDataStore.saveUserType(User.NO_GOOGLE)
- }
-
- suspend fun logout() {
- appLoungeDataStore.destroyCredentials()
- appLoungeDataStore.saveUserType(null)
- // reset app source preferences on logout.
- appLoungePreference.run {
- enableOpenSource()
- enablePwa()
- enablePlayStore()
- }
- }
-}
diff --git a/app/src/main/java/foundation/e/apps/data/login/PlayStoreAuthenticator.kt b/app/src/main/java/foundation/e/apps/data/login/PlayStoreAuthenticator.kt
deleted file mode 100644
index 8de91e2994c6c9d20088d55294c9948b2135d436..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/PlayStoreAuthenticator.kt
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2019-2025 e Foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- */
-
-package foundation.e.apps.data.login
-
-import android.content.Context
-import com.aurora.gplayapi.data.models.AuthData
-import dagger.hilt.android.qualifiers.ApplicationContext
-import foundation.e.apps.data.ResultSupreme
-import foundation.e.apps.data.enums.ResultStatus
-import foundation.e.apps.data.enums.User
-import foundation.e.apps.data.login.api.GoogleLoginManager
-import foundation.e.apps.data.login.api.PlayStoreLoginManager
-import foundation.e.apps.data.login.api.PlayStoreLoginManagerFactory
-import foundation.e.apps.data.login.api.PlayStoreLoginWrapper
-import foundation.e.apps.data.preference.AppLoungeDataStore
-import foundation.e.apps.data.preference.AppLoungePreference
-import foundation.e.apps.data.preference.getSync
-import foundation.e.apps.data.retryWithBackoff
-import kotlinx.serialization.json.Json
-import timber.log.Timber
-import java.util.Locale
-import javax.inject.Inject
-import javax.inject.Singleton
-
-/**
- * Class to get GPlay auth data. Call [login] to get an already saved auth data
- * or to fetch a new one for first use. Handles auth validation internally.
- *
- * https://gitlab.e.foundation/e/backlog/-/issues/5680
- */
-@Singleton
-class PlayStoreAuthenticator @Inject constructor(
- @ApplicationContext private val context: Context,
- private val json: Json,
- private val appLoungeDataStore: AppLoungeDataStore,
- private val appLoungePreference: AppLoungePreference,
-) : StoreAuthenticator, AuthDataValidator {
-
- @Inject
- lateinit var loginManagerFactory: PlayStoreLoginManagerFactory
-
- private val user: User
- get() = appLoungeDataStore.getUser()
-
- private val loginManager: PlayStoreLoginManager
- get() = loginManagerFactory.createLoginManager(user)
-
- private val loginWrapper: PlayStoreLoginWrapper
- get() = PlayStoreLoginWrapper(loginManager, user)
-
- private val locale: Locale
- get() = context.resources.configuration.locales[0]
-
- override fun isStoreActive(): Boolean {
- if (user == User.UNAVAILABLE) {
- /*
- * UNAVAILABLE user means first login is not completed.
- */
- return false
- }
- return appLoungePreference.isPlayStoreSelected()
- }
-
- /**
- * Main entry point to get GPlay auth data.
- */
- override suspend fun login(): AuthObject.GPlayAuth {
- val savedAuth = getSavedAuthData()
-
- val authData = savedAuth ?: run {
- // if no saved data, then generate new auth data.
- val result = retryWithBackoff {
- generateAuthData()
- }
-
- result?.let {
- if (it.isSuccess()) it.data!!
- else return AuthObject.GPlayAuth(it, user)
- }
- }
-
- val formattedAuthData = authData?.let { formatAuthData(it) }
- formattedAuthData?.locale = locale
- val result: ResultSupreme = ResultSupreme.create(
- status = ResultStatus.OK,
- data = formattedAuthData
- )
- result.otherPayload = formattedAuthData?.email
-
- if (savedAuth == null && formattedAuthData != null) {
- saveAuthData(formattedAuthData)
- }
-
- return AuthObject.GPlayAuth(result, user)
- }
-
- override suspend fun logout() {
- appLoungeDataStore.saveAuthData(null)
- }
-
- /**
- * Get authData stored as JSON and convert to AuthData class.
- * Returns null if nothing is saved.
- */
- private fun getSavedAuthData(): AuthData? {
- val authJson = appLoungeDataStore.authData.getSync()
- return if (authJson.isBlank()) null
- else try {
- json.decodeFromString(authJson)
- } catch (e: Exception) {
- e.printStackTrace()
- null
- }
- }
-
- private suspend fun saveAuthData(authData: AuthData) {
- appLoungeDataStore.saveAuthData(authData)
- }
-
- /**
- * Generate new AuthData based on the user type.
- */
- private suspend fun generateAuthData(): ResultSupreme {
- return when (appLoungeDataStore.getUser()) {
- User.ANONYMOUS -> getAuthDataAnonymously()
- User.GOOGLE -> getAuthDataWithGoogleAccount()
- else -> ResultSupreme.Error("User type not ANONYMOUS or GOOGLE")
- }
- }
-
- /**
- * Aurora OSS GPlay API complains of missing headers sometimes.
- * Converting [authData] to Json and back to [AuthData] fixed it.
- */
- private fun formatAuthData(authData: AuthData): AuthData {
- val localAuthDataJson = json.encodeToString(authData)
- return json.decodeFromString(localAuthDataJson)
- }
-
- private suspend fun getAuthDataAnonymously(): ResultSupreme {
- return loginWrapper.login(locale).run {
- if (isSuccess()) ResultSupreme.Success(formatAuthData(this.data!!))
- else this
- }
- }
-
- private suspend fun getAuthDataWithGoogleAccount(): ResultSupreme {
-
- val email = appLoungeDataStore.emailData.getSync()
- val oauthToken = appLoungeDataStore.oauthToken.getSync()
- val aasToken = appLoungeDataStore.aasToken.getSync()
- /*
- * If aasToken is not blank, means it was stored successfully from a previous Google login.
- * Use it to fetch auth data.
- */
- if (aasToken.isNotBlank()) {
- return loginWrapper.login(locale)
- }
-
- /*
- * If aasToken is not yet saved / made, fetch it from email and oauthToken.
- */
- val aasTokenResponse = loginWrapper.getAasToken(
- loginManager as GoogleLoginManager,
- email,
- oauthToken
- )
-
- /*
- * If fetch was unsuccessful, return blank auth data.
- * We replicate from the response, so that it will carry on any error message if present
- * in the aasTokenResponse.
- */
- if (!aasTokenResponse.isSuccess()) {
- return ResultSupreme.replicate(aasTokenResponse, null)
- }
-
- val aasTokenFetched = aasTokenResponse.data ?: ""
-
- if (aasTokenFetched.isBlank()) {
- return ResultSupreme.Error("Fetched AAS Token is blank")
- }
-
- /*
- * Finally save the aasToken and create auth data.
- */
- appLoungeDataStore.saveAasToken(aasTokenFetched)
- return loginWrapper.login(locale).run {
- if (isSuccess()) ResultSupreme.Success(formatAuthData(this.data!!))
- else this
- }
- }
-
- override suspend fun validateAuthData(): ResultSupreme {
- val savedAuth = getSavedAuthData()
- if (!isAuthDataValid(savedAuth)) {
- Timber.i("Validating AuthData...")
- val authData = generateAuthData()
- authData.data?.let {
- saveAuthData(it)
- return authData
- }
- return ResultSupreme.create(ResultStatus.UNKNOWN)
- }
-
- return ResultSupreme.create(ResultStatus.OK, savedAuth)
- }
-
- private suspend fun isAuthDataValid(savedAuth: AuthData?) =
- savedAuth != null && loginWrapper.validate(savedAuth).exception == null
-}
diff --git a/app/src/main/java/foundation/e/apps/data/login/StoreAuthenticator.kt b/app/src/main/java/foundation/e/apps/data/login/StoreAuthenticator.kt
deleted file mode 100644
index bc0d18106951b98abf0728abc28e39ba51842764..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/StoreAuthenticator.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2019-2022 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package foundation.e.apps.data.login
-
-/**
- * Store (Google Play Store, Clean Apk) authenticator.
- */
-interface StoreAuthenticator {
- suspend fun login(): AuthObject
- suspend fun logout()
- fun isStoreActive(): Boolean
-}
diff --git a/app/src/main/java/foundation/e/apps/data/login/api/AnonymousLoginManager.kt b/app/src/main/java/foundation/e/apps/data/login/api/AnonymousLoginManager.kt
deleted file mode 100644
index 08f81649e7dc16eefcadc048e1c6182e87d78bbd..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/api/AnonymousLoginManager.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2019-2025 e Foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- */
-
-package foundation.e.apps.data.login.api
-
-import com.aurora.gplayapi.data.models.AuthData
-import com.aurora.gplayapi.data.models.PlayResponse
-import com.aurora.gplayapi.helpers.AuthHelper
-import foundation.e.apps.data.login.Auth
-import foundation.e.apps.data.playstore.utils.CustomAuthValidator
-import foundation.e.apps.data.playstore.utils.GPlayHttpClient
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
-import kotlinx.serialization.json.Json
-import java.util.Locale
-import java.util.Properties
-
-class AnonymousLoginManager(
- private val gPlayHttpClient: GPlayHttpClient,
- private val nativeDeviceProperty: Properties,
- private val json: Json,
-) : PlayStoreLoginManager {
-
- private val tokenUrl: String = "https://eu.gtoken.ecloud.global"
-
- /**
- * Log anonymously a user
- *
- * @return authData: authentication data
- */
- override suspend fun login(): AuthData? {
- var authData: AuthData? = null
- withContext(Dispatchers.IO) {
- val response = gPlayHttpClient.postAuth(
- tokenUrl, json.encodeToString(nativeDeviceProperty).toByteArray()
- )
- if (response.code != 200 || !response.isSuccessful) {
- throw Exception(
- "Error fetching Anonymous credentials\n" +
- "Network code: ${response.code}\n" +
- "Success: ${response.isSuccessful}" +
- response.errorString.run {
- if (isNotBlank()) "\nError message: $this"
- else ""
- }
- )
- } else {
- val auth = json.decodeFromString(String(response.responseBytes))
- authData = AuthHelper.build(
- email = auth.email,
- token = auth.auth,
- tokenType = AuthHelper.Token.AUTH,
- isAnonymous = true,
- properties = nativeDeviceProperty,
- locale = Locale.getDefault()
- )
- }
- }
- return authData
- }
-
- /**
- * Check if an AuthData is valid. Returns a [PlayResponse].
- * Check [PlayResponse.isSuccessful] to see if the validation was successful.
- */
- override suspend fun validate(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
- }
-}
diff --git a/app/src/main/java/foundation/e/apps/data/login/api/GoogleLoginManager.kt b/app/src/main/java/foundation/e/apps/data/login/api/GoogleLoginManager.kt
deleted file mode 100644
index 757ea13a49c3d62df84c5db17d8543e211880c3e..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/api/GoogleLoginManager.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2019-2022 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package foundation.e.apps.data.login.api
-
-import com.aurora.gplayapi.data.models.AuthData
-import com.aurora.gplayapi.data.models.PlayResponse
-import com.aurora.gplayapi.helpers.AuthHelper
-import foundation.e.apps.data.playstore.utils.AC2DMTask
-import foundation.e.apps.data.playstore.utils.CustomAuthValidator
-import foundation.e.apps.data.playstore.utils.GPlayHttpClient
-import foundation.e.apps.data.preference.AppLoungeDataStore
-import foundation.e.apps.data.preference.getSync
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
-import java.util.Properties
-
-class GoogleLoginManager(
- private val gPlayHttpClient: GPlayHttpClient,
- private val nativeDeviceProperty: Properties,
- private val aC2DMTask: AC2DMTask,
- private val appLoungeDataStore: AppLoungeDataStore,
-) : PlayStoreLoginManager {
-
- /**
- * Get PlayResponse for AC2DM Map. This allows us to get an error message too.
- *
- * An aasToken is extracted from this map. This is passed to [login]
- * to generate AuthData. This token is very important as it cannot be regenerated,
- * hence it must be saved for future use.
- *
- * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5680
- */
- suspend fun getAC2DMResponse(email: String, oauthToken: String): PlayResponse {
- var response: PlayResponse
- withContext(Dispatchers.IO) {
- response = aC2DMTask.getAC2DMResponse(email, oauthToken)
- }
- return response
- }
-
- /**
- * Login
- *
- * @return authData: authentication data
- */
- override suspend fun login(): AuthData? {
- val email = appLoungeDataStore.emailData.getSync()
- val aasToken = appLoungeDataStore.aasToken.getSync()
-
- var authData: AuthData?
- withContext(Dispatchers.IO) {
- authData = AuthHelper.build(email, aasToken, tokenType = AuthHelper.Token.AAS, properties = nativeDeviceProperty)
- }
- return authData
- }
-
- /**
- * Check if an AuthData is valid. Returns a [PlayResponse].
- * Check [PlayResponse.isSuccessful] to see if the validation was successful.
- */
- override suspend fun validate(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
- }
-}
diff --git a/app/src/main/java/foundation/e/apps/data/login/api/PlayStoreLoginManager.kt b/app/src/main/java/foundation/e/apps/data/login/api/PlayStoreLoginManager.kt
deleted file mode 100644
index 12fad6b6d7ea8b93204fcabb77d82544dcbf26ad..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/api/PlayStoreLoginManager.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2019-2022 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package foundation.e.apps.data.login.api
-
-import com.aurora.gplayapi.data.models.AuthData
-import com.aurora.gplayapi.data.models.PlayResponse
-
-interface PlayStoreLoginManager {
- suspend fun login(): AuthData?
- suspend fun validate(authData: AuthData): PlayResponse
-}
diff --git a/app/src/main/java/foundation/e/apps/data/login/api/PlayStoreLoginManagerFactory.kt b/app/src/main/java/foundation/e/apps/data/login/api/PlayStoreLoginManagerFactory.kt
deleted file mode 100644
index 121b9d626204d838899ad61b85e9962b86006ca8..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/api/PlayStoreLoginManagerFactory.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2019-2022 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package foundation.e.apps.data.login.api
-
-import foundation.e.apps.data.enums.User
-import foundation.e.apps.data.playstore.utils.AC2DMTask
-import foundation.e.apps.data.playstore.utils.GPlayHttpClient
-import foundation.e.apps.data.preference.AppLoungeDataStore
-import kotlinx.serialization.json.Json
-import java.util.Properties
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class PlayStoreLoginManagerFactory @Inject constructor(
- private val gPlayHttpClient: GPlayHttpClient,
- private val nativeDeviceProperty: Properties,
- private val aC2DMTask: AC2DMTask,
- private val json: Json,
- private val appLoungeDataStore: AppLoungeDataStore,
-) {
-
- fun createLoginManager(user: User): PlayStoreLoginManager {
- return when (user) {
- User.GOOGLE -> GoogleLoginManager(gPlayHttpClient, nativeDeviceProperty, aC2DMTask, appLoungeDataStore)
- else -> AnonymousLoginManager(gPlayHttpClient, nativeDeviceProperty, json)
- }
- }
-}
diff --git a/app/src/main/java/foundation/e/apps/data/login/api/PlayStoreLoginWrapper.kt b/app/src/main/java/foundation/e/apps/data/login/api/PlayStoreLoginWrapper.kt
deleted file mode 100644
index bf8820e024f1cefa62fa67208341426bb8704e6e..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/api/PlayStoreLoginWrapper.kt
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2019-2022 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package foundation.e.apps.data.login.api
-
-import com.aurora.gplayapi.data.models.AuthData
-import com.aurora.gplayapi.data.models.PlayResponse
-import foundation.e.apps.data.ResultSupreme
-import foundation.e.apps.data.enums.User
-import foundation.e.apps.data.handleNetworkResult
-import foundation.e.apps.data.login.exceptions.GPlayLoginException
-import foundation.e.apps.data.playstore.utils.AC2DMUtil
-import foundation.e.apps.utils.eventBus.AppEvent
-import foundation.e.apps.utils.eventBus.EventBus
-import java.util.Locale
-
-/**
- * Call methods of [GoogleLoginManager] and [AnonymousLoginManager] from here.
- *
- * Dependency Injection via hilt is not possible,
- * we need to manually check login type, create an instance of either [GoogleLoginManager]
- * or [AnonymousLoginManager] and pass it to [loginManager].
- *
- * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5680
- */
-class PlayStoreLoginWrapper constructor(
- private val loginManager: PlayStoreLoginManager,
- private val user: User,
-) {
-
- /**
- * Gets the auth data from instance of [PlayStoreLoginManager].
- */
- suspend fun login(locale: Locale): ResultSupreme {
- val result = handleNetworkResult {
- loginManager.login()
- }
- return result.apply {
- this.data?.locale = locale
- this.exception = when (result) {
- is ResultSupreme.Timeout -> GPlayLoginException(true, "GPlay API timeout", user)
- is ResultSupreme.Error -> GPlayLoginException(false, result.message, user)
- else -> {
- EventBus.invokeEvent(AppEvent.SuccessfulLogin(user))
- null
- }
- }
- }
- }
-
- /**
- * Get AuthData validity of in the form of PlayResponse.
- * Advantage of not using a simple boolean is we get error message and
- * network code of the request inside PlayResponse object.
- *
- * Applicable for both Google and Anonymous login.
- *
- * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5680
- */
- suspend fun validate(authData: AuthData): ResultSupreme {
- var response = PlayResponse()
- val result = handleNetworkResult {
- response = loginManager.validate(authData)
- if (response.code != 200) {
- throw Exception("Validation network code: ${response.code}")
- }
- response
- }
- return ResultSupreme.replicate(result, response).apply {
- this.exception = when (result) {
- is ResultSupreme.Timeout -> GPlayLoginException(true, "GPlay API timeout", user)
- is ResultSupreme.Error -> GPlayLoginException(false, result.message, user)
- else -> null
- }
- }
- }
-
- /**
- * Gets email and oauthToken from Google login, finds the AASToken from AC2DM response
- * and returns it. This token is then used to fetch AuthData from [login].
- *
- * Do note that for a given oauthToken, it has been observed that AASToken can
- * only be generated once. So this token must be saved for future use.
- *
- * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5680
- *
- * @param googleAccountLoginManager An instance of [GoogleLoginManager] must be passed, this method
- * cannot work on [loginManager] as it is a common interface for both Google and Anonymous
- * login, but this method is only for Google login.
- */
- suspend fun getAasToken(
- googleAccountLoginManager: GoogleLoginManager,
- email: String,
- oauthToken: String
- ): ResultSupreme {
- val result = handleNetworkResult {
- var aasToken = ""
- val response = googleAccountLoginManager.getAC2DMResponse(email, oauthToken)
- var error = response.errorString
- if (response.isSuccessful) {
- val responseMap = AC2DMUtil.parseResponse(String(response.responseBytes))
- aasToken = responseMap["Token"] ?: ""
- if (aasToken.isBlank() && error.isBlank()) {
- error = "AASToken not found in map."
- }
- }
- /*
- * Default value of PlayResponse.errorString is "No Error".
- * https://gitlab.com/AuroraOSS/gplayapi/-/blob/master/src/main/java/com/aurora/gplayapi/data/models/PlayResponse.kt
- */
- if (error != "No Error") {
- throw Exception(error)
- }
- aasToken
- }
- return result.apply {
- this.exception = when (result) {
- is ResultSupreme.Timeout -> GPlayLoginException(true, "GPlay API timeout", User.GOOGLE)
- is ResultSupreme.Error -> GPlayLoginException(false, result.message, User.GOOGLE)
- else -> null
- }
- }
- }
-}
diff --git a/app/src/main/java/foundation/e/apps/data/playstore/utils/AC2DMTask.kt b/app/src/main/java/foundation/e/apps/data/playstore/GoogleLoginDataSource.kt
similarity index 50%
rename from app/src/main/java/foundation/e/apps/data/playstore/utils/AC2DMTask.kt
rename to app/src/main/java/foundation/e/apps/data/playstore/GoogleLoginDataSource.kt
index f1e3eb3eb785c80a96759fdc4dc492a0460509b0..8f21743a2eb6715416ba0af889f3ba2ad97f6ae7 100644
--- a/app/src/main/java/foundation/e/apps/data/playstore/utils/AC2DMTask.kt
+++ b/app/src/main/java/foundation/e/apps/data/playstore/GoogleLoginDataSource.kt
@@ -1,30 +1,18 @@
-/*
- * Apps Quickly and easily install Android apps onto your device!
- * Copyright (C) 2021, Rahul Kumar Patel
- * Copyright (C) 2021 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
+package foundation.e.apps.data.playstore
-package foundation.e.apps.data.playstore.utils
-
-import com.aurora.gplayapi.data.models.PlayResponse
+import com.aurora.gplayapi.data.models.AuthData
+import com.aurora.gplayapi.helpers.AuthHelper
+import foundation.e.apps.data.login.exceptions.GPlayException
+import foundation.e.apps.data.playstore.utils.AC2DMUtil
+import foundation.e.apps.data.playstore.utils.GPlayHttpClient
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.Locale
+import java.util.Properties
import javax.inject.Inject
+import javax.inject.Singleton
-class AC2DMTask @Inject constructor(
+@Singleton
+class GoogleLoginDataSource @Inject constructor(
private val gPlayHttpClient: GPlayHttpClient
) {
companion object {
@@ -33,10 +21,7 @@ class AC2DMTask @Inject constructor(
private const val PLAY_SERVICES_VERSION_CODE = 19629032
}
- fun getAC2DMResponse(email: String?, oAuthToken: String?): PlayResponse {
- if (email == null || oAuthToken == null)
- return PlayResponse()
-
+ fun getAasToken(email: String, oAuthToken: String): String {
val params: MutableMap = hashMapOf()
params["lang"] = Locale.getDefault().toString().replace("_", "-")
params["google_play_services_version"] = PLAY_SERVICES_VERSION_CODE
@@ -62,6 +47,27 @@ class AC2DMTask @Inject constructor(
* Returning PlayResponse instead of map so that we can get the network response code.
* Issue: https://gitlab.e.foundation/e/backlog/-/issues/5709
*/
- return gPlayHttpClient.post(TOKEN_AUTH_URL, header, body.toRequestBody())
+ val response = gPlayHttpClient.post(TOKEN_AUTH_URL, header, body.toRequestBody())
+ var aasToken = ""
+ var error = response.errorString
+ if (response.isSuccessful) {
+ val responseMap = AC2DMUtil.parseResponse(String(response.responseBytes))
+ aasToken = responseMap["Token"] ?: ""
+ if (aasToken.isBlank() && error.isBlank()) {
+ error = "AASToken not found in map."
+ }
+ }
+ /*
+ * Default value of PlayResponse.errorString is "No Error".
+ * https://gitlab.com/AuroraOSS/gplayapi/-/blob/master/src/main/java/com/aurora/gplayapi/data/models/PlayResponse.kt
+ */
+ if (error != "No Error") {
+ throw GPlayException(false, error)
+ }
+ return aasToken
+ }
+
+ suspend fun googleLogin(email: String, aasToken: String, nativeDeviceProperty: Properties): AuthData {
+ return AuthHelper.build(email, aasToken, tokenType = AuthHelper.Token.AAS, properties = nativeDeviceProperty)
}
}
diff --git a/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt b/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt
index 2775e3291203f663162d819acd2312629946842f..92093ad285622c67cd3e9f0039b4499214e3a0f0 100644
--- a/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt
+++ b/app/src/main/java/foundation/e/apps/data/playstore/PlayStoreRepository.kt
@@ -19,10 +19,13 @@
package foundation.e.apps.data.playstore
import android.content.Context
+import com.aurora.gplayapi.GooglePlayApi
+import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.Category
import com.aurora.gplayapi.data.models.ContentRating
import com.aurora.gplayapi.data.models.PlayFile
import com.aurora.gplayapi.data.models.StreamCluster
+import com.aurora.gplayapi.data.providers.HeaderProvider
import com.aurora.gplayapi.helpers.AppDetailsHelper
import com.aurora.gplayapi.helpers.ContentRatingHelper
import com.aurora.gplayapi.helpers.PurchaseHelper
@@ -42,27 +45,78 @@ import foundation.e.apps.data.application.search.SearchSuggestion
import foundation.e.apps.data.application.utils.CategoryType
import foundation.e.apps.data.application.utils.toApplication
import foundation.e.apps.data.enums.Source
+import foundation.e.apps.data.enums.User
import foundation.e.apps.data.handleNetworkResult
-import foundation.e.apps.data.login.AuthenticatorRepository
-import foundation.e.apps.data.login.PlayStoreAuthenticator
import foundation.e.apps.data.playstore.utils.GPlayHttpClient
import foundation.e.apps.data.playstore.utils.GplayHttpRequestException
+import foundation.e.apps.data.preference.AppLoungeDataStore
import foundation.e.apps.utils.SystemInfoProvider
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
+import kotlinx.serialization.json.Json
import timber.log.Timber
+import java.util.Properties
import javax.inject.Inject
import com.aurora.gplayapi.data.models.App as GplayApp
-@Suppress("TooManyFunctions")
+@Suppress("TooManyFunctions", "LongParameterList")
class PlayStoreRepository @Inject constructor(
@ApplicationContext private val context: Context,
private val gPlayHttpClient: GPlayHttpClient,
- private val authenticatorRepository: AuthenticatorRepository,
private val applicationDataManager: ApplicationDataManager,
- private val playStoreSearchHelper: PlayStoreSearchHelper
+ private val playStoreSearchHelper: PlayStoreSearchHelper,
+ private val appLoungeDataStore: AppLoungeDataStore,
+ private val tokenDispenserDataSource: TokenDispenserDataSource,
+ private val googleLoginDataSource: GoogleLoginDataSource,
+ private val nativeDeviceProperty: Properties,
+ private val json: Json,
) : StoreRepository {
+ suspend fun updateToken(): AuthData {
+ val userType = appLoungeDataStore.getUser()
+
+ val rawAuthToken = when (userType) {
+ User.GOOGLE -> fetchGoogleLoginToken()
+ User.ANONYMOUS -> tokenDispenserDataSource.fetchGToken(nativeDeviceProperty)
+ else -> error("User type not ANONYMOUS or GOOGLE")
+ }
+
+ val authData = formatAuthData(rawAuthToken)
+
+ // Force locale to be the one configured on the device.
+ // But didn't we expect to have the one of the Google Account here ?
+ authData.locale = context.resources.configuration.locales[0]
+
+ appLoungeDataStore.saveAuthData(authData)
+
+ return authData
+ }
+
+ private suspend fun fetchGoogleLoginToken(): AuthData = withContext(Dispatchers.IO) {
+ val email = appLoungeDataStore.emailData.first()
+ var aasToken: String = appLoungeDataStore.aasToken.first()
+
+ /*
+ * If aasToken is not blank, means it was stored successfully from a previous Google login.
+ * We will use it to fetch auth data. Otherwie, fetch it from email and oauthToken.
+ */
+ if (aasToken.isBlank()) {
+ aasToken = googleLoginDataSource.getAasToken(email, appLoungeDataStore.oauthToken.first())
+ appLoungeDataStore.saveAasToken(aasToken)
+ }
+
+ googleLoginDataSource.googleLogin(email, aasToken, nativeDeviceProperty)
+ }
+
+ /**
+ * Aurora OSS GPlay API complains of missing headers sometimes.
+ * Converting [authData] to Json and back to [AuthData] fixed it.
+ */
+ private fun formatAuthData(authData: AuthData): AuthData {
+ val localAuthDataJson = json.encodeToString(authData)
+ return json.decodeFromString(localAuthDataJson)
+ }
override suspend fun getHomeScreenData(list: MutableList): List {
val homeScreenData = mutableMapOf>()
@@ -199,16 +253,17 @@ class PlayStoreRepository @Inject constructor(
appDetails.toApplication(context)
}
- private fun getAppDetailsHelper(): AppDetailsHelper {
- val authData = authenticatorRepository.getGPlayAuthOrThrow()
- val appDetailsHelper = AppDetailsHelper(authData).using(gPlayHttpClient)
+ private suspend fun getAppDetailsHelper(): AppDetailsHelper {
+ return doAuthenticatedRequest { authData ->
+ val appDetailsHelper = AppDetailsHelper(authData).using(gPlayHttpClient)
- return appDetailsHelper
+ appDetailsHelper
+ }
}
private suspend fun refreshPlayStoreAuthentication() {
Timber.i("Refreshing authentication.")
- authenticatorRepository.fetchAuthObjects(listOf(PlayStoreAuthenticator::class.java.simpleName))
+ updateToken()
}
suspend fun getAppDetailsWeb(packageName: String): Application? {
@@ -270,15 +325,18 @@ class PlayStoreRepository @Inject constructor(
throw IllegalStateException("Could not get download details for $idOrPackageName")
}
- // Don't store auth data in a variable. Always get GPlay auth from repository,
- // because auth might get refreshed while fetching app details.
- val purchaseHelper = PurchaseHelper(authenticatorRepository.getGPlayAuthOrThrow())
- .using(gPlayHttpClient)
+ doAuthenticatedRequest { authData ->
+
+ // Don't store auth data in a variable. Always get GPlay auth from repository,
+ // because auth might get refreshed while fetching app details.
+ val purchaseHelper = PurchaseHelper(authData)
+ .using(gPlayHttpClient)
- buildList { addAll(purchaseHelper.purchase(idOrPackageName, version, offer)) }
+ buildList { addAll(purchaseHelper.purchase(idOrPackageName, version, offer)) }
+ }
}
- fun isAnonymousUser() = authenticatorRepository.getGPlayAuthOrThrow().isAnonymous
+ fun isAnonymousUser() = appLoungeDataStore.getAuthData().isAnonymous
suspend fun getOnDemandModule(
packageName: String,
@@ -286,39 +344,64 @@ class PlayStoreRepository @Inject constructor(
versionCode: Long,
offerType: Int
): List {
- val downloadData = mutableListOf()
- val authData = authenticatorRepository.getGPlayAuthOrThrow()
-
- withContext(Dispatchers.IO) {
- val purchaseHelper = PurchaseHelper(authData).using(gPlayHttpClient)
- downloadData.addAll(
- purchaseHelper.purchase(packageName, versionCode, offerType, moduleName)
- )
+ return doAuthenticatedRequest { authData ->
+ val downloadData = mutableListOf()
+
+ withContext(Dispatchers.IO) {
+ val purchaseHelper = PurchaseHelper(authData).using(gPlayHttpClient)
+ downloadData.addAll(
+ purchaseHelper.purchase(packageName, versionCode, offerType, moduleName)
+ )
+ }
+ downloadData
}
- return downloadData
}
suspend fun getContentRatingWithId(
appPackage: String,
contentRating: ContentRating
): ContentRating {
- val authData = authenticatorRepository.getGPlayAuthOrThrow()
- val contentRatingHelper = ContentRatingHelper(authData)
-
- return withContext(Dispatchers.IO) {
- contentRatingHelper.updateContentRatingWithId(
- appPackage,
- contentRating
- )
+ return doAuthenticatedRequest { authData ->
+ val contentRatingHelper = ContentRatingHelper(authData)
+
+ withContext(Dispatchers.IO) {
+ contentRatingHelper.updateContentRatingWithId(
+ appPackage,
+ contentRating
+ )
+ }
}
}
suspend fun getEnglishContentRating(packageName: String): ContentRating {
- val authData = authenticatorRepository.getGPlayAuthOrThrow()
- val contentRatingHelper = ContentRatingHelper(authData)
+ return doAuthenticatedRequest { authData ->
+ val contentRatingHelper = ContentRatingHelper(authData)
- return withContext(Dispatchers.IO) {
- contentRatingHelper.getEnglishContentRating(packageName)
+ withContext(Dispatchers.IO) {
+ contentRatingHelper.getEnglishContentRating(packageName)
+ }
+ }
+ }
+
+ private suspend fun doAuthenticatedRequest(request: suspend (AuthData) -> T): T {
+ val authData = appLoungeDataStore.getAuthData()
+ return request(authData)
+ }
+
+ // Helper function, to detect error not specific to the request
+ // (authentication stale, network unreachable, ...)
+ // This can be used to workaround Exception swallowing done by GPlayAPI library.
+ suspend fun sendCommonErrorsProbe() {
+ withContext(Dispatchers.IO) {
+ val endpoint: String = GooglePlayApi.URL_SYNC
+ val headers = HeaderProvider.getDefaultHeaders(appLoungeDataStore.getAuthData())
+
+ // No expectation for the response result, just look for the exceptions:
+ // will send on EventBus error for 401, 429.
+ // will throw GplayHttpRequestException for all response code "not 200 or 429".
+ // will throw SocketTimeoutException as GplayHttpRequestException 408
+ // will throw all other exceptions.
+ gPlayHttpClient.post(endpoint, headers, hashMapOf())
}
}
}
diff --git a/app/src/main/java/foundation/e/apps/data/playstore/TokenDispenserDataSource.kt b/app/src/main/java/foundation/e/apps/data/playstore/TokenDispenserDataSource.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9d127fde62e26918d21b2238d1f5479816ebc967
--- /dev/null
+++ b/app/src/main/java/foundation/e/apps/data/playstore/TokenDispenserDataSource.kt
@@ -0,0 +1,60 @@
+package foundation.e.apps.data.playstore
+
+import com.aurora.gplayapi.data.models.AuthData
+import com.aurora.gplayapi.helpers.AuthHelper
+import foundation.e.apps.data.login.Auth
+import foundation.e.apps.data.login.exceptions.GPlayException
+import foundation.e.apps.data.playstore.utils.GPlayHttpClient
+import foundation.e.apps.data.playstore.utils.GPlayHttpClient.Companion.STATUS_CODE_OK
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import kotlinx.serialization.json.Json
+import java.util.Locale
+import java.util.Properties
+import javax.inject.Inject
+import javax.inject.Singleton
+
+// TODO: 20251208: this DataSource should be merge with foundation.e.apps.data.ecloud.EcloudRepository
+@Singleton
+class TokenDispenserDataSource @Inject constructor(
+ private val gPlayHttpClient: GPlayHttpClient,
+ private val json: Json,
+) {
+
+ private val tokenUrl: String = "https://eu.gtoken.ecloud.global"
+
+ /**
+ * Log anonymously a user
+ *
+ * @return authData: authentication data
+ */
+ suspend fun fetchGToken(nativeDeviceProperty: Properties): AuthData {
+ return withContext(Dispatchers.IO) {
+ val response = gPlayHttpClient.postAuth(
+ tokenUrl,
+ json.encodeToString(nativeDeviceProperty).toByteArray()
+ )
+ if (response.code != STATUS_CODE_OK || !response.isSuccessful) {
+ throw GPlayException(
+ false,
+ "Error fetching Anonymous credentials\n" +
+ "Network code: ${response.code}\n" +
+ "Success: ${response.isSuccessful}" +
+ response.errorString.run {
+ if (isNotBlank()) "\nError message: $this" else ""
+ }
+ )
+ } else {
+ val auth = json.decodeFromString(String(response.responseBytes))
+ AuthHelper.build(
+ email = auth.email,
+ token = auth.auth,
+ tokenType = AuthHelper.Token.AUTH,
+ isAnonymous = true,
+ properties = nativeDeviceProperty,
+ locale = Locale.getDefault()
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/apps/data/playstore/utils/CustomAuthValidator.kt b/app/src/main/java/foundation/e/apps/data/playstore/utils/CustomAuthValidator.kt
deleted file mode 100644
index e029c196e32685470aeef02d021a98d7456fd1cd..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/playstore/utils/CustomAuthValidator.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2019-2022 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package foundation.e.apps.data.playstore.utils
-
-import com.aurora.gplayapi.GooglePlayApi
-import com.aurora.gplayapi.data.models.AuthData
-import com.aurora.gplayapi.data.models.PlayResponse
-import com.aurora.gplayapi.data.providers.HeaderProvider
-import com.aurora.gplayapi.helpers.NativeHelper
-import com.aurora.gplayapi.network.IHttpClient
-
-/**
- * Custom implementation of [AuthValidator].
- * This returns [PlayResponse] for [getValidityResponse],
- * which is a replacement of [AuthValidator.isValid] which just returned a boolean.
- * A PlayResponse object allows us to look into whether the request had any network error.
- *
- * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5709
- */
-class CustomAuthValidator(authData: AuthData) : NativeHelper(authData) {
-
- override fun using(httpClient: IHttpClient) = apply {
- this.httpClient = httpClient
- }
-
- fun getValidityResponse(): PlayResponse {
- val endpoint: String = GooglePlayApi.URL_SYNC
- val headers = HeaderProvider.getDefaultHeaders(authData)
- return httpClient.post(endpoint, headers, hashMapOf())
- }
-}
diff --git a/app/src/main/java/foundation/e/apps/data/playstore/utils/GPlayHttpClient.kt b/app/src/main/java/foundation/e/apps/data/playstore/utils/GPlayHttpClient.kt
index 235e9ab52a58b156ea88c2e8f6b1d7ca1f6c0066..0f504158a77e95e8ccf6032677d5bff247e012a6 100644
--- a/app/src/main/java/foundation/e/apps/data/playstore/utils/GPlayHttpClient.kt
+++ b/app/src/main/java/foundation/e/apps/data/playstore/utils/GPlayHttpClient.kt
@@ -56,7 +56,7 @@ class GPlayHttpClient @Inject constructor(
private const val HTTP_METHOD_POST = "POST"
private const val HTTP_METHOD_GET = "GET"
private const val SEARCH_SUGGEST = "searchSuggest"
- private const val STATUS_CODE_OK = 200
+ const val STATUS_CODE_OK = 200
const val STATUS_CODE_UNAUTHORIZED = 401
const val STATUS_CODE_TOO_MANY_REQUESTS = 429
private const val URL_SUBSTRING_PURCHASE = "purchase"
diff --git a/app/src/main/java/foundation/e/apps/data/preference/AppLoungeDataStore.kt b/app/src/main/java/foundation/e/apps/data/preference/AppLoungeDataStore.kt
index 871f9fae3a242c5ac9ff4d562c23ffed0f2d6320..8d8e9606f56f893272467ed1483eb4a421ef57a2 100644
--- a/app/src/main/java/foundation/e/apps/data/preference/AppLoungeDataStore.kt
+++ b/app/src/main/java/foundation/e/apps/data/preference/AppLoungeDataStore.kt
@@ -71,7 +71,7 @@ class AppLoungeDataStore @Inject constructor(
private val TOCSTATUS = booleanPreferencesKey("tocStatus")
private val TOSVERSION = stringPreferencesKey("tosversion")
- val authData = context.dataStore.data.map { it[AUTHDATA] ?: "" }
+ val rawAuthData = context.dataStore.data.map { it[AUTHDATA] ?: "" }
val emailData = context.dataStore.data.map { it[EMAIL] ?: "" }
val oauthToken = context.dataStore.data.map { it[OAUTHTOKEN] ?: "" }
val aasToken = context.dataStore.data.map { it[AASTOKEN] ?: "" }
@@ -90,7 +90,7 @@ class AppLoungeDataStore @Inject constructor(
}
fun getAuthData(): AuthData {
- val authData = authData.getSync()
+ val authData = rawAuthData.getSync()
return if (authData.isEmpty()) {
AuthData("", "")
} else {
diff --git a/app/src/main/java/foundation/e/apps/di/LoginModule.kt b/app/src/main/java/foundation/e/apps/di/LoginModule.kt
deleted file mode 100644
index ac964187dbbfebeb94c51bb6e2773ebff850829c..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/di/LoginModule.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2019-2022 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package foundation.e.apps.di
-
-import dagger.Module
-import dagger.Provides
-import dagger.hilt.InstallIn
-import dagger.hilt.components.SingletonComponent
-import foundation.e.apps.data.login.CleanApkAuthenticator
-import foundation.e.apps.data.login.PlayStoreAuthenticator
-import foundation.e.apps.data.login.StoreAuthenticator
-
-@InstallIn(SingletonComponent::class)
-@Module
-object LoginModule {
-
- @Provides
- fun providesAuthenticators(
- gPlay: PlayStoreAuthenticator,
- cleanApk: CleanApkAuthenticator,
- ): List {
- return listOf(gPlay, cleanApk)
- }
-}
diff --git a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt
index ab94f3cd25315a1346bd6afd35fb3481309759ad..3b7e1dd112c922b05595003843f04893e82be3a7 100644
--- a/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt
+++ b/app/src/main/java/foundation/e/apps/domain/ValidateAppAgeLimitUseCase.kt
@@ -32,7 +32,7 @@ import foundation.e.apps.data.parentalcontrol.ParentalControlRepository.Companio
import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository
import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup
import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingRepository
-import foundation.e.apps.domain.model.ContentRatingValidity
+import foundation.e.apps.domain.entities.ContentRatingValidity
import org.e.parentalcontrol.data.model.TypeAppManagement
import timber.log.Timber
import javax.inject.Inject
diff --git a/app/src/main/java/foundation/e/apps/domain/model/ContentRatingValidity.kt b/app/src/main/java/foundation/e/apps/domain/entities/ContentRatingValidity.kt
similarity index 95%
rename from app/src/main/java/foundation/e/apps/domain/model/ContentRatingValidity.kt
rename to app/src/main/java/foundation/e/apps/domain/entities/ContentRatingValidity.kt
index 25f6ee48480d9d413fbd2fc5e7315dd2d3b5dac5..a7b66026602f6e0587984225e0c0629259da7ddc 100644
--- a/app/src/main/java/foundation/e/apps/domain/model/ContentRatingValidity.kt
+++ b/app/src/main/java/foundation/e/apps/domain/entities/ContentRatingValidity.kt
@@ -17,7 +17,7 @@
*
*/
-package foundation.e.apps.domain.model
+package foundation.e.apps.domain.entities
import com.aurora.gplayapi.data.models.ContentRating
diff --git a/app/src/main/java/foundation/e/apps/domain/usecases/LoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/usecases/LoginUseCase.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d7518144572ced335bfb49446c7bf8ea162cf79e
--- /dev/null
+++ b/app/src/main/java/foundation/e/apps/domain/usecases/LoginUseCase.kt
@@ -0,0 +1,110 @@
+package foundation.e.apps.domain.usecases
+
+import com.aurora.gplayapi.data.models.AuthData
+import foundation.e.apps.data.ResultSupreme
+import foundation.e.apps.data.enums.ResultStatus
+import foundation.e.apps.data.enums.User
+import foundation.e.apps.data.login.AuthObject
+import foundation.e.apps.data.playstore.PlayStoreRepository
+import foundation.e.apps.data.preference.AppLoungeDataStore
+import foundation.e.apps.data.preference.AppLoungePreference
+import foundation.e.apps.di.qualifiers.IoCoroutineScope
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class LoginUseCase @Inject constructor(
+ private val playStoreRepository: PlayStoreRepository,
+ private val appLoungeDataStore: AppLoungeDataStore,
+ private val appLoungePreference: AppLoungePreference,
+ @IoCoroutineScope private val ioCoroutineScope: CoroutineScope,
+) {
+ suspend fun setGoogleUser(email: String, oauth: String) {
+ withContext(ioCoroutineScope.coroutineContext) {
+ appLoungeDataStore.saveGoogleLogin(email, oauth)
+ appLoungeDataStore.saveUserType(User.GOOGLE)
+ }
+ }
+
+ suspend fun setAnonymousUser() {
+ withContext(ioCoroutineScope.coroutineContext) {
+ appLoungeDataStore.saveUserType(User.ANONYMOUS)
+ }
+ }
+
+ suspend fun setNoGoogleMode() {
+ withContext(ioCoroutineScope.coroutineContext) {
+ appLoungePreference.run {
+ disablePlayStore()
+ enableOpenSource()
+ enablePwa()
+ }
+ appLoungeDataStore.saveUserType(User.NO_GOOGLE)
+ }
+ }
+
+ suspend fun fetchAuthObjects(forceRefresh: Boolean): List {
+ return withContext(ioCoroutineScope.coroutineContext) {
+ val userType = appLoungeDataStore.getUser()
+
+ if (!forceRefresh) {
+ // use saved state.
+ val savedAuth = appLoungeDataStore.getAuthData()
+ if (savedAuth != AuthData("", "")) {
+ return@withContext buildAuthObjectList(Result.success(savedAuth), userType = userType)
+ }
+ }
+
+ val result = runCatching { playStoreRepository.updateToken() }
+
+ buildAuthObjectList(result, userType)
+ }
+ }
+
+ private fun buildAuthObjectList(gPlayloginResult: Result, userType: User): List {
+ val authObjectsLocal = ArrayList()
+
+ // Google Login case:
+ if (userType == User.GOOGLE || userType == User.ANONYMOUS) {
+ val result: ResultSupreme
+ if (gPlayloginResult.isSuccess) {
+ result = ResultSupreme.create(
+ status = ResultStatus.OK,
+ data = gPlayloginResult.getOrNull()
+ )
+ result.otherPayload = gPlayloginResult.getOrNull()?.email
+ } else {
+ result = ResultSupreme.create(
+ status = ResultStatus.UNKNOWN,
+ exception = gPlayloginResult.exceptionOrNull() as? Exception
+ )
+ }
+
+ authObjectsLocal.add(AuthObject.GPlayAuth(result, userType))
+ }
+
+ // Clean apk
+ if (
+ userType != User.UNAVAILABLE && (
+ appLoungePreference.isOpenSourceSelected() || appLoungePreference.isPWASelected()
+ )
+ ) {
+ authObjectsLocal.add(AuthObject.CleanApk(ResultSupreme.Success(Unit), userType,))
+ }
+
+ return authObjectsLocal
+ }
+
+ suspend fun logout() {
+ appLoungeDataStore.destroyCredentials()
+ appLoungeDataStore.saveUserType(null)
+ // reset app source preferences on logout.
+ appLoungePreference.run {
+ enableOpenSource()
+ enablePwa()
+ enablePlayStore()
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt
index f2907200fad013b69d98450d14be62fa2262d5ab..0015741c813c4f6b8019bfc11872d40348b4b789 100644
--- a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt
+++ b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt
@@ -21,7 +21,7 @@ import foundation.e.apps.data.blockedApps.BlockedAppRepository
import foundation.e.apps.data.enums.ResultStatus
import foundation.e.apps.data.enums.User
import foundation.e.apps.data.gitlab.SystemAppsUpdatesRepository
-import foundation.e.apps.data.login.AuthenticatorRepository
+import foundation.e.apps.data.playstore.PlayStoreRepository
import foundation.e.apps.data.preference.AppLoungeDataStore
import foundation.e.apps.data.updates.UpdatesManagerRepository
import foundation.e.apps.install.workmanager.AppInstallProcessor
@@ -32,16 +32,17 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import timber.log.Timber
+@Suppress("LongParameterList")
@HiltWorker
class UpdatesWorker @AssistedInject constructor(
@Assisted private val context: Context,
@Assisted private val params: WorkerParameters,
private val updatesManagerRepository: UpdatesManagerRepository,
private val appLoungeDataStore: AppLoungeDataStore,
- private val authenticatorRepository: AuthenticatorRepository,
private val appInstallProcessor: AppInstallProcessor,
private val blockedAppRepository: BlockedAppRepository,
private val systemAppsUpdatesRepository: SystemAppsUpdatesRepository,
+ private val playStoreRepository: PlayStoreRepository,
) : CoroutineWorker(context, params) {
companion object {
@@ -109,7 +110,9 @@ class UpdatesWorker @AssistedInject constructor(
val isConnectedToUnMeteredNetwork = isConnectedToUnMeteredNetwork(applicationContext)
val appsNeededToUpdate = mutableListOf()
val user = getUser()
- val authData = authenticatorRepository.getValidatedAuthData().data
+ val authData = appLoungeDataStore.getAuthData().takeIf {
+ runCatching { playStoreRepository.sendCommonErrorsProbe() }.isSuccess
+ }
val resultStatus: ResultStatus
if (user in listOf(User.ANONYMOUS, User.GOOGLE) && authData != null) {
diff --git a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt
index 2831499ed01872614a8a2575a72fb0e2b647e437..07e66676ffefdfaf6658cd05b72b65c77a7e3ecf 100644
--- a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt
+++ b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt
@@ -36,7 +36,7 @@ import foundation.e.apps.data.install.models.AppInstall
import foundation.e.apps.data.playstore.utils.GplayHttpRequestException
import foundation.e.apps.data.preference.AppLoungeDataStore
import foundation.e.apps.domain.ValidateAppAgeLimitUseCase
-import foundation.e.apps.domain.model.ContentRatingValidity
+import foundation.e.apps.domain.entities.ContentRatingValidity
import foundation.e.apps.install.AppInstallComponents
import foundation.e.apps.install.download.DownloadManagerUtils
import foundation.e.apps.install.notification.StorageNotificationManager
diff --git a/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt b/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt
index e6785d4a221c030752a2af5a547bba313aa2d42b..cbad8dce2190c8f472071e7ebed09b7a5e5ebd63 100644
--- a/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt
+++ b/app/src/main/java/foundation/e/apps/provider/AgeRatingProvider.kt
@@ -46,15 +46,13 @@ import foundation.e.apps.data.ResultSupreme
import foundation.e.apps.data.blockedApps.BlockedAppRepository
import foundation.e.apps.data.enums.Source
import foundation.e.apps.data.install.models.AppInstall
-import foundation.e.apps.data.login.AuthenticatorRepository
-import foundation.e.apps.data.login.exceptions.GPlayLoginException
import foundation.e.apps.data.parentalcontrol.ContentRatingDao
import foundation.e.apps.data.parentalcontrol.ContentRatingEntity
import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository
import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingRepository
import foundation.e.apps.data.preference.AppLoungeDataStore
import foundation.e.apps.domain.ValidateAppAgeLimitUseCase
-import foundation.e.apps.domain.model.ContentRatingValidity
+import foundation.e.apps.domain.entities.ContentRatingValidity
import foundation.e.apps.install.pkg.AppLoungePackageManager
import foundation.e.apps.utils.isNetworkAvailable
import kotlinx.coroutines.Dispatchers.IO
@@ -69,7 +67,6 @@ class AgeRatingProvider : ContentProvider() {
@EntryPoint
@InstallIn(SingletonComponent::class)
interface ContentProviderEntryPoint {
- fun provideAuthenticationRepository(): AuthenticatorRepository
fun providePackageManager(): AppLoungePackageManager
fun provideGPlayContentRatingsRepository(): GPlayContentRatingRepository
fun provideFDroidAntiFeatureRepository(): FDroidAntiFeatureRepository
@@ -86,7 +83,6 @@ class AgeRatingProvider : ContentProvider() {
private const val AUTHORITY = "foundation.e.apps.provider"
}
- private lateinit var authenticatorRepository: AuthenticatorRepository
private lateinit var appLoungePackageManager: AppLoungePackageManager
private lateinit var gPlayContentRatingRepository: GPlayContentRatingRepository
private lateinit var fDroidAntiFeatureRepository: FDroidAntiFeatureRepository
@@ -213,10 +209,11 @@ class AgeRatingProvider : ContentProvider() {
* Setup AuthData for other APIs to access,
* if user has logged in with Google or Anonymous mode.
*/
+ // TODO 20251204 : this method doesn't make any sense.
private suspend fun initAuthData() {
val authData = appLoungeDataStore.getAuthData()
if (authData.email.isNotBlank() && authData.authToken.isNotBlank()) {
- authenticatorRepository.setGPlayAuth(authData)
+ appLoungeDataStore.saveAuthData(authData)
}
}
@@ -301,9 +298,9 @@ class AgeRatingProvider : ContentProvider() {
private fun hasAuthData(): Boolean {
return try {
- authenticatorRepository.getGPlayAuthOrThrow()
+ appLoungeDataStore.getAuthData()
true
- } catch (e: GPlayLoginException) {
+ } catch (e: NoSuchElementException) {
Timber.e("No AuthData to check content rating")
false
}
@@ -318,7 +315,6 @@ class AgeRatingProvider : ContentProvider() {
val hiltEntryPoint =
EntryPointAccessors.fromApplication(appContext, ContentProviderEntryPoint::class.java)
- authenticatorRepository = hiltEntryPoint.provideAuthenticationRepository()
appLoungePackageManager = hiltEntryPoint.providePackageManager()
gPlayContentRatingRepository = hiltEntryPoint.provideGPlayContentRatingsRepository()
fDroidAntiFeatureRepository = hiltEntryPoint.provideFDroidAntiFeatureRepository()
diff --git a/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt b/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt
index b523b4330aa1af5f0a16f3b81851041fa1d0fff8..37f413ca10cf56014c33f42fc7a6d303c59c7eaf 100644
--- a/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt
+++ b/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt
@@ -51,7 +51,7 @@ class DumpAuthData : BroadcastReceiver() {
private fun getAuthDataDump(context: Context): String {
val json = Json
// TODO: replace with context.configuration
- val authData = AppLoungeDataStore(context, json).authData.getSync().let {
+ val authData = AppLoungeDataStore(context, json).rawAuthData.getSync().let {
json.decodeFromString(it)
}
val filteredData = JSONObject().apply {
diff --git a/app/src/main/java/foundation/e/apps/ui/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/ui/LoginViewModel.kt
index 211e45bdfe3813c8dd448692c1ea669a5b467feb..dc46059606e1a3bdd2ae6a4fd68cc693d24ddc3d 100644
--- a/app/src/main/java/foundation/e/apps/ui/LoginViewModel.kt
+++ b/app/src/main/java/foundation/e/apps/ui/LoginViewModel.kt
@@ -23,9 +23,8 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import foundation.e.apps.data.Stores
import foundation.e.apps.data.enums.Source
-import foundation.e.apps.data.enums.User
import foundation.e.apps.data.login.AuthObject
-import foundation.e.apps.data.login.AuthenticatorRepository
+import foundation.e.apps.domain.usecases.LoginUseCase
import foundation.e.apps.ui.parentFragment.LoadingViewModel
import kotlinx.coroutines.launch
import okhttp3.Cache
@@ -37,7 +36,7 @@ import javax.inject.Inject
*/
@HiltViewModel
class LoginViewModel @Inject constructor(
- private val authenticatorRepository: AuthenticatorRepository,
+ private val loginUseCase: LoginUseCase,
private val cache: Cache,
private val stores: Stores
) : ViewModel() {
@@ -57,9 +56,9 @@ class LoginViewModel @Inject constructor(
/**
* Main point of starting of entire authentication process.
*/
- fun startLoginFlow(clearList: List = listOf()) {
+ fun startLoginFlow(forceRefresh: Boolean = false) {
viewModelScope.launch {
- val authObjectsLocal = authenticatorRepository.fetchAuthObjects(clearList)
+ val authObjectsLocal = loginUseCase.fetchAuthObjects(forceRefresh)
authObjects.postValue(authObjectsLocal)
}
}
@@ -72,7 +71,7 @@ class LoginViewModel @Inject constructor(
fun initialAnonymousLogin(onUserSaved: () -> Unit) {
viewModelScope.launch {
stores.enableStore(Source.PLAY_STORE)
- authenticatorRepository.saveUserType(User.ANONYMOUS)
+ loginUseCase.setAnonymousUser()
onUserSaved()
startLoginFlow()
}
@@ -87,8 +86,7 @@ class LoginViewModel @Inject constructor(
fun initialGoogleLogin(email: String, oauthToken: String, onUserSaved: () -> Unit) {
viewModelScope.launch {
stores.enableStore(Source.PLAY_STORE)
- authenticatorRepository.saveGoogleLogin(email, oauthToken)
- authenticatorRepository.saveUserType(User.GOOGLE)
+ loginUseCase.setGoogleUser(email, oauthToken)
onUserSaved()
startLoginFlow()
}
@@ -104,7 +102,7 @@ class LoginViewModel @Inject constructor(
fun initialNoGoogleLogin(onUserSaved: () -> Unit) {
viewModelScope.launch {
stores.disableStore(Source.PLAY_STORE)
- authenticatorRepository.setNoGoogleMode()
+ loginUseCase.setNoGoogleMode()
onUserSaved()
startLoginFlow()
}
@@ -139,7 +137,7 @@ class LoginViewModel @Inject constructor(
fun logout() {
viewModelScope.launch {
cache.evictAll()
- authenticatorRepository.logout()
+ loginUseCase.logout()
authObjects.postValue(listOf())
}
}
diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt
index 74e3468891c429e83f14a740fba440b594176a44..f93417665cf4724eef2742da0c10accaa37e61d6 100644
--- a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt
+++ b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt
@@ -51,7 +51,6 @@ import foundation.e.apps.data.Constants
import foundation.e.apps.data.enums.User
import foundation.e.apps.data.install.models.AppInstall
import foundation.e.apps.data.login.AuthObject
-import foundation.e.apps.data.login.PlayStoreAuthenticator
import foundation.e.apps.data.login.exceptions.GPlayValidationException
import foundation.e.apps.databinding.ActivityMainBinding
import foundation.e.apps.install.updates.UpdatesNotifier
@@ -182,7 +181,7 @@ class MainActivity : AppCompatActivity() {
}
private fun refreshSession() {
- loginViewModel.startLoginFlow(listOf(PlayStoreAuthenticator::class.java.simpleName))
+ loginViewModel.startLoginFlow(forceRefresh = true)
}
fun isInitialScreen(): Boolean {
diff --git a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt
index 42c17f9762c2558501dcf7bd4fdc1dd813924dc0..841815c1d41bb5ecb2a4ff81913821f4f336da56 100644
--- a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt
+++ b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt
@@ -34,7 +34,6 @@ import androidx.lifecycle.ViewModelProvider
import foundation.e.apps.R
import foundation.e.apps.data.enums.User
import foundation.e.apps.data.login.AuthObject
-import foundation.e.apps.data.login.PlayStoreAuthenticator
import foundation.e.apps.data.login.exceptions.CleanApkException
import foundation.e.apps.data.login.exceptions.CleanApkIOException
import foundation.e.apps.data.login.exceptions.GPlayException
@@ -210,7 +209,7 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) {
* Clears saved GPlay AuthData and restarts login process to get
*/
fun clearAndRestartGPlayLogin() {
- loginViewModel.startLoginFlow(listOf(PlayStoreAuthenticator::class.java.simpleName))
+ loginViewModel.startLoginFlow(forceRefresh = true)
}
/**
diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/LocaleChangedBroadcastReceiver.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/LocaleChangedBroadcastReceiver.kt
index e8fd90ba5ad5d92f96afc11483a3faf182a3674e..385d61d535a50b3fb2171a9f8c9d7062e301e40e 100644
--- a/app/src/main/java/foundation/e/apps/ui/setup/signin/LocaleChangedBroadcastReceiver.kt
+++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/LocaleChangedBroadcastReceiver.kt
@@ -54,7 +54,7 @@ class LocaleChangedBroadcastReceiver : BroadcastReceiver() {
}
coroutineScope.launch {
try {
- val authDataJson = appLoungeDataStore.authData.getSync()
+ val authDataJson = appLoungeDataStore.rawAuthData.getSync()
val authData = json.decodeFromString(authDataJson)
authData.locale = context.resources.configuration.locales[0]
appLoungeDataStore.saveAuthData(authData)
diff --git a/app/src/test/java/foundation/e/apps/data/playstore/PlayStoreRepositoryTest.kt b/app/src/test/java/foundation/e/apps/data/playstore/PlayStoreRepositoryTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c57a360ab760bdf7d30cd2851450409725fbc9ac
--- /dev/null
+++ b/app/src/test/java/foundation/e/apps/data/playstore/PlayStoreRepositoryTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2025 MURENA SAS
+ * Apps Quickly and easily install Android apps onto your device!
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package foundation.e.apps.data.playstore
+
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.os.LocaleList
+import com.aurora.gplayapi.data.models.AuthData
+import foundation.e.apps.data.application.ApplicationDataManager
+import foundation.e.apps.data.enums.User
+import foundation.e.apps.data.preference.AppLoungeDataStore
+import foundation.e.apps.data.playstore.utils.GPlayHttpClient
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.slot
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import kotlinx.serialization.json.Json
+import org.junit.Before
+import org.junit.Test
+import java.util.Locale
+import java.util.Properties
+import kotlin.test.assertEquals
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class PlayStoreRepositoryTest {
+
+ private val context: Context = mockk()
+ private val resources: Resources = mockk()
+ private val configuration: Configuration = mockk()
+ private val locales: LocaleList = mockk()
+
+ private val gPlayHttpClient: GPlayHttpClient = mockk(relaxed = true)
+ private val applicationDataManager: ApplicationDataManager = mockk(relaxed = true)
+ private val playStoreSearchHelper: PlayStoreSearchHelper = mockk(relaxed = true)
+ private val appLoungeDataStore: AppLoungeDataStore = mockk(relaxed = true)
+ private val tokenDispenserDataSource: TokenDispenserDataSource = mockk(relaxed = true)
+ private val googleLoginDataSource: GoogleLoginDataSource = mockk(relaxed = true)
+ private val nativeDeviceProperty = Properties().apply {
+ put("os", "android")
+ }
+ private val json = Json { encodeDefaults = true }
+
+ private lateinit var repository: PlayStoreRepository
+ private val expectedLocale = Locale.FRANCE
+
+ @Before
+ fun setup() {
+ every { context.resources } returns resources
+ every { resources.configuration } returns configuration
+ every { configuration.locales } returns locales
+ every { locales[0] } returns expectedLocale
+
+ repository = PlayStoreRepository(
+ context,
+ gPlayHttpClient,
+ applicationDataManager,
+ playStoreSearchHelper,
+ appLoungeDataStore,
+ tokenDispenserDataSource,
+ googleLoginDataSource,
+ nativeDeviceProperty,
+ json
+ )
+ }
+
+ @Test
+ fun `updateToken fetches anonymous token`() = runTest {
+ val expectedAuthData = AuthData("anon@example.com", "anonymous-token")
+ every { appLoungeDataStore.getUser() } returns User.ANONYMOUS
+ coEvery { tokenDispenserDataSource.fetchGToken(nativeDeviceProperty) } returns expectedAuthData
+
+ val result = repository.updateToken()
+
+ assertEquals(expectedAuthData.email, result.email)
+ assertEquals(expectedAuthData.authToken, result.authToken)
+
+ coVerify(exactly = 1) { tokenDispenserDataSource.fetchGToken(nativeDeviceProperty) }
+ coVerify(exactly = 1) { appLoungeDataStore.saveAuthData(result) }
+ }
+
+ @Test
+ fun `updateToken set locale and format and save authdata`() = runTest {
+ val expectedAuthData = AuthData("anon@example.com", "anonymous-token")
+ every { appLoungeDataStore.getUser() } returns User.ANONYMOUS
+ coEvery { tokenDispenserDataSource.fetchGToken(nativeDeviceProperty) } returns expectedAuthData
+
+ val result = repository.updateToken()
+
+ assertEquals(expectedAuthData.email, result.email)
+ assertEquals(expectedAuthData.authToken, result.authToken)
+ assertEquals(expectedLocale, result.locale)
+
+ coVerify(exactly = 1) { appLoungeDataStore.saveAuthData(result) }
+ }
+
+ @Test
+ fun `updateToken fetches google login token when aas token present`() = runTest {
+ val email = "user@google.com"
+ val oauthToken = "oauth-token"
+ val aasToken = "aas-token"
+ val expectedAuthData = AuthData(email, "google-token")
+ every { appLoungeDataStore.getUser() } returns User.GOOGLE
+ every { appLoungeDataStore.emailData } returns flowOf(email)
+ every { appLoungeDataStore.aasToken } returns flowOf(aasToken)
+ every { appLoungeDataStore.oauthToken } returns flowOf(oauthToken)
+ coEvery { googleLoginDataSource.googleLogin(email, aasToken, nativeDeviceProperty) } returns expectedAuthData
+
+ val result = repository.updateToken()
+
+ assertEquals(email, result.email)
+ assertEquals(expectedLocale, result.locale)
+
+ coVerify(exactly = 1) { googleLoginDataSource.googleLogin(email, aasToken, nativeDeviceProperty) }
+ coVerify(exactly = 1) { appLoungeDataStore.saveAuthData(result) }
+
+ coVerify(exactly = 0) { tokenDispenserDataSource.fetchGToken(any()) }
+ }
+
+ @Test
+ fun `updateToken aas token when missing`() = runTest {
+ val email = "user@google.com"
+ val oauthToken = "oauth-token"
+ val newAasToken = "new-aas-token"
+ val expectedAuthData = AuthData(email, "google-token")
+ every { appLoungeDataStore.getUser() } returns User.GOOGLE
+ every { appLoungeDataStore.emailData } returns flowOf(email)
+ every { appLoungeDataStore.aasToken } returns flowOf("")
+ every { appLoungeDataStore.oauthToken } returns flowOf(oauthToken)
+ coEvery { googleLoginDataSource.getAasToken(email, oauthToken) } returns newAasToken
+ coEvery { appLoungeDataStore.saveAasToken(newAasToken) } returns Unit
+ coEvery { googleLoginDataSource.googleLogin(email, newAasToken, nativeDeviceProperty) } returns expectedAuthData
+
+ repository.updateToken()
+
+ coVerify(exactly = 1) { googleLoginDataSource.getAasToken(email, oauthToken) }
+ coVerify(exactly = 1) { appLoungeDataStore.saveAasToken(newAasToken) }
+ coVerify(exactly = 1) { googleLoginDataSource.googleLogin(email, newAasToken, nativeDeviceProperty) }
+ }
+}
diff --git a/app/src/test/java/foundation/e/apps/domain/usecases/LoginUseCaseTest.kt b/app/src/test/java/foundation/e/apps/domain/usecases/LoginUseCaseTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c19db5b08dc055c883a6c134c55e2f34ab0dcfef
--- /dev/null
+++ b/app/src/test/java/foundation/e/apps/domain/usecases/LoginUseCaseTest.kt
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2025 MURENA SAS
+ * Apps Quickly and easily install Android apps onto your device!
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.apps.domain.usecases
+
+import com.aurora.gplayapi.data.models.AuthData
+import foundation.e.apps.data.enums.ResultStatus
+import foundation.e.apps.data.enums.User
+import foundation.e.apps.data.login.AuthObject
+import foundation.e.apps.data.playstore.PlayStoreRepository
+import foundation.e.apps.data.preference.AppLoungeDataStore
+import foundation.e.apps.data.preference.AppLoungePreference
+import io.mockk.clearMocks
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class LoginUseCaseTest {
+
+ private val playStoreRepository: PlayStoreRepository = mockk(relaxed = true)
+ private val appLoungeDataStore: AppLoungeDataStore = mockk(relaxed = true)
+ private val appLoungePreference: AppLoungePreference = mockk(relaxed = true)
+
+ @Before
+ fun setup() {
+ clearMocks(playStoreRepository, appLoungeDataStore, appLoungePreference)
+ }
+
+ private fun createLoginUseCase(scope: CoroutineScope) =
+ LoginUseCase(
+ playStoreRepository,
+ appLoungeDataStore,
+ appLoungePreference,
+ scope,
+ )
+
+ @Test
+ fun `setGoogleUser persists credentials and user type`() = runTest {
+ val loginUseCase = createLoginUseCase(this)
+
+ loginUseCase.setGoogleUser("foo@example.com", "oauth-token")
+
+ coVerify(exactly = 1) { appLoungeDataStore.saveGoogleLogin("foo@example.com", "oauth-token") }
+ coVerify(exactly = 1) { appLoungeDataStore.saveUserType(User.GOOGLE) }
+ }
+
+ @Test
+ fun `setAnonymousUser stores google configuration`() = runTest {
+ val loginUseCase = createLoginUseCase(this)
+
+ loginUseCase.setAnonymousUser()
+
+ coVerify(exactly = 1) { appLoungeDataStore.saveUserType(User.ANONYMOUS) }
+ }
+
+ @Test
+ fun `setNoGoogleMode toggles preferences and saves no google user`() = runTest {
+ val loginUseCase = createLoginUseCase(this)
+
+ loginUseCase.setNoGoogleMode()
+
+ verify(exactly = 1) {
+ appLoungePreference.disablePlayStore()
+ appLoungePreference.enableOpenSource()
+ appLoungePreference.enablePwa()
+ }
+ coVerify(exactly = 1) { appLoungeDataStore.saveUserType(User.NO_GOOGLE) }
+ }
+
+ @Test
+ fun `fetchAuthObjects reuses saved auth when available`() = runTest {
+ val loginUseCase = createLoginUseCase(this)
+ val savedAuth = AuthData("user@example.com", "saved-token")
+
+ every { appLoungeDataStore.getUser() } returns User.GOOGLE
+ coEvery { appLoungeDataStore.getAuthData() } returns savedAuth
+ every { appLoungePreference.isOpenSourceSelected() } returns true
+ every { appLoungePreference.isPWASelected() } returns false
+
+ val authObjects = loginUseCase.fetchAuthObjects(forceRefresh = false)
+
+ assertEquals(2, authObjects.size)
+ assertTrue(authObjects.any { it is AuthObject.GPlayAuth })
+ assertTrue(authObjects.any { it is AuthObject.CleanApk })
+ coVerify(exactly = 0) { playStoreRepository.updateToken() }
+ }
+
+ @Test
+ fun `fetchAuthObjects refreshes token when forced`() = runTest {
+ val loginUseCase = createLoginUseCase(this)
+
+ val refreshedAuth = AuthData("user@example.com", "fresh-token")
+
+ every { appLoungeDataStore.getUser() } returns User.GOOGLE
+ every { appLoungeDataStore.getAuthData() } returns AuthData("", "")
+ every { appLoungePreference.isOpenSourceSelected() } returns true
+ every { appLoungePreference.isPWASelected() } returns true
+ coEvery { playStoreRepository.updateToken() } returns refreshedAuth
+
+ val authObjects = loginUseCase.fetchAuthObjects(forceRefresh = true)
+
+ assertEquals(2, authObjects.size)
+ assertTrue(authObjects.any { it is AuthObject.GPlayAuth })
+ assertTrue(authObjects.any { it is AuthObject.CleanApk })
+ coVerify(exactly = 1) { playStoreRepository.updateToken() }
+ }
+
+ @Test
+ fun `fetchAuthObjects return gplay errors when refresh fails`() = runTest {
+ val loginUseCase = createLoginUseCase(this)
+
+ every { appLoungeDataStore.getUser() } returns User.GOOGLE
+ every { appLoungePreference.isOpenSourceSelected() } returns false
+ every { appLoungePreference.isPWASelected() } returns true
+ coEvery { playStoreRepository.updateToken() } throws IllegalStateException("boom")
+
+ val authObjects = loginUseCase.fetchAuthObjects(forceRefresh = true)
+
+ assertEquals(2, authObjects.size)
+ assertTrue(authObjects.any { it is AuthObject.GPlayAuth && it.result?.getResultStatus() == ResultStatus.UNKNOWN })
+ assertTrue(authObjects.any { it is AuthObject.CleanApk })
+
+ }
+
+ @Test
+ fun `fetchAuthObjects doesnt returns cleanapk when PWA and OpenSource disabled`() = runTest {
+ val loginUseCase = createLoginUseCase(this)
+
+ val savedAuth = AuthData("user@example.com", "saved-token")
+
+ every { appLoungeDataStore.getUser() } returns User.ANONYMOUS
+ coEvery { appLoungeDataStore.getAuthData() } returns savedAuth
+ every { appLoungePreference.isOpenSourceSelected() } returns false
+ every { appLoungePreference.isPWASelected() } returns false
+
+ val authObjects = loginUseCase.fetchAuthObjects(forceRefresh = true)
+ assertEquals(1, authObjects.size)
+ assertTrue(authObjects.none { it is AuthObject.CleanApk })
+ }
+
+
+ @Test
+ fun `fetchAuthObjects doesnt returns gplay when disabled`() = runTest {
+ val loginUseCase = createLoginUseCase(this)
+
+
+ every { appLoungeDataStore.getUser() } returns User.NO_GOOGLE
+ every { appLoungePreference.isOpenSourceSelected() } returns true
+ every { appLoungePreference.isPWASelected() } returns false
+
+ val authObjects = loginUseCase.fetchAuthObjects(forceRefresh = true)
+ assertEquals(1, authObjects.size)
+ assertTrue(authObjects.first() is AuthObject.CleanApk )
+ }
+
+ @Test
+ fun `logout clears credentials and restores preferences`() = runTest {
+ val loginUseCase = createLoginUseCase(this)
+ loginUseCase.logout()
+
+ coVerify(exactly = 1) { appLoungeDataStore.destroyCredentials() }
+ coVerify(exactly = 1) { appLoungeDataStore.saveUserType(null) }
+ verify(exactly = 1) {
+ appLoungePreference.enableOpenSource()
+ appLoungePreference.enablePwa()
+ appLoungePreference.enablePlayStore()
+ }
+ }
+}
diff --git a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt
index a11efa092d2ec44beca293c126d1421b503004ee..919530224e091c2e169dfbb5e6430ca315c6b375 100644
--- a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt
+++ b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt
@@ -30,7 +30,7 @@ import foundation.e.apps.data.install.AppManager
import foundation.e.apps.data.install.models.AppInstall
import foundation.e.apps.data.preference.AppLoungeDataStore
import foundation.e.apps.domain.ValidateAppAgeLimitUseCase
-import foundation.e.apps.domain.model.ContentRatingValidity
+import foundation.e.apps.domain.entities.ContentRatingValidity
import foundation.e.apps.install.AppInstallComponents
import foundation.e.apps.install.notification.StorageNotificationManager
import foundation.e.apps.install.workmanager.AppInstallProcessor
diff --git a/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt
index c3f13a29c5e057cde103f78271125dc64f4471d7..aa4383c4e6bc4ce6f90fc16a56c3109da1b02598 100644
--- a/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt
+++ b/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt
@@ -24,7 +24,7 @@ import foundation.e.apps.data.ResultSupreme
import foundation.e.apps.data.Stores
import foundation.e.apps.data.enums.User
import foundation.e.apps.data.login.AuthObject
-import foundation.e.apps.data.login.AuthenticatorRepository
+import foundation.e.apps.domain.usecases.LoginUseCase
import foundation.e.apps.ui.LoginViewModel
import okhttp3.Cache
import org.junit.Before
@@ -36,7 +36,7 @@ import org.mockito.MockitoAnnotations
class LoginViewModelTest {
@Mock
- private lateinit var authenticatorRepository: AuthenticatorRepository
+ private lateinit var loginUseCase: LoginUseCase
@Mock
private lateinit var cache: Cache
@Mock
@@ -51,7 +51,7 @@ class LoginViewModelTest {
@Before
fun setup() {
MockitoAnnotations.openMocks(this)
- loginViewModel = LoginViewModel(authenticatorRepository, cache, stores)
+ loginViewModel = LoginViewModel(loginUseCase, cache, stores)
}
@Test