diff --git a/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt b/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt
index 2a4ee09891f02fecbfd545080095a7a45ee3fe2b..3fcf7e4291a670066c7431b8e8bd17270079b7cc 100644
--- a/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt
+++ b/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt
@@ -43,6 +43,8 @@ import timber.log.Timber
import timber.log.Timber.Forest.plant
import java.util.concurrent.Executors
import javax.inject.Inject
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.runBlocking
@HiltAndroidApp
@DelicateCoroutinesApi
@@ -74,7 +76,7 @@ class AppLoungeApplication : Application(), Configuration.Provider {
val pkgManagerBR = object : PkgManagerBR() {}
registerReceiver(pkgManagerBR, pkgManagerModule.getFilter(), RECEIVER_EXPORTED)
- val currentVersion = dataStoreModule.getTOSVersion()
+ val currentVersion = runBlocking { dataStoreModule.tosVersion.first() }
if (!currentVersion.contentEquals(TOS_VERSION)) {
MainScope().launch {
dataStoreModule.saveTOCStatus(false, "")
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
index d3879f586f72ad3bc0deb82588888d421acd4ef6..5a83b9a83c1628a75df9e1d7e0c19047ddcd433d 100644
--- a/app/src/main/java/foundation/e/apps/data/login/CleanApkAuthenticator.kt
+++ b/app/src/main/java/foundation/e/apps/data/login/CleanApkAuthenticator.kt
@@ -19,6 +19,8 @@ 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.DataStoreModule
+import foundation.e.apps.data.preference.PreferenceManagerModule
import javax.inject.Inject
import javax.inject.Singleton
@@ -28,11 +30,12 @@ import javax.inject.Singleton
*/
@Singleton
class CleanApkAuthenticator @Inject constructor(
- val loginData: LoginData,
+ private val dataStoreModule: DataStoreModule,
+ private val preferenceManagerModule: PreferenceManagerModule,
) : StoreAuthenticator {
private val user: User
- get() = loginData.getUserType()
+ get() = dataStoreModule.getUserType()
override fun isStoreActive(): Boolean {
if (user == User.UNAVAILABLE) {
@@ -41,7 +44,7 @@ class CleanApkAuthenticator @Inject constructor(
*/
return false
}
- return loginData.isOpenSourceSelected() || loginData.isPWASelected()
+ return preferenceManagerModule.isOpenSourceSelected() || preferenceManagerModule.isPWASelected()
}
override suspend fun login(): AuthObject.CleanApk {
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
index b041a29db6f07b62f8ce4d3818989ee053ed3adb..8af0004e3e73a18c63eeb2a73a18dea0874f496f 100644
--- a/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt
+++ b/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt
@@ -19,6 +19,8 @@ package foundation.e.apps.data.login
import foundation.e.apps.data.Constants
import foundation.e.apps.data.enums.User
+import foundation.e.apps.data.preference.DataStoreModule
+import foundation.e.apps.data.preference.PreferenceManagerModule
import javax.inject.Inject
import javax.inject.Singleton
@@ -30,33 +32,34 @@ import javax.inject.Singleton
*/
@Singleton
class LoginCommon @Inject constructor(
- private val loginData: LoginData,
+ private val dataStoreModule: DataStoreModule,
+ private val preferenceManagerModule: PreferenceManagerModule,
) {
suspend fun saveUserType(user: User) {
- loginData.saveUserType(user)
+ dataStoreModule.saveUserType(user)
}
fun getUserType(): User {
- return loginData.getUserType()
+ return dataStoreModule.getUserType()
}
suspend fun saveGoogleLogin(email: String, oauth: String) {
- loginData.saveGoogleLogin(email, oauth)
+ dataStoreModule.saveGoogleLogin(email, oauth)
}
suspend fun setNoGoogleMode() {
- loginData.setSource(Constants.PREFERENCE_SHOW_FOSS, true)
- loginData.setSource(Constants.PREFERENCE_SHOW_PWA, true)
- loginData.setSource(Constants.PREFERENCE_SHOW_GPLAY, false)
- loginData.saveUserType(User.NO_GOOGLE)
+ preferenceManagerModule.setSource(Constants.PREFERENCE_SHOW_FOSS, true)
+ preferenceManagerModule.setSource(Constants.PREFERENCE_SHOW_PWA, true)
+ preferenceManagerModule.setSource(Constants.PREFERENCE_SHOW_GPLAY, false)
+ dataStoreModule.saveUserType(User.NO_GOOGLE)
}
suspend fun logout() {
- loginData.destroyCredentials()
- loginData.clearUserType()
+ dataStoreModule.destroyCredentials()
+ dataStoreModule.saveUserType(null)
// reset app source preferences on logout.
- loginData.setSource(Constants.PREFERENCE_SHOW_FOSS, true)
- loginData.setSource(Constants.PREFERENCE_SHOW_PWA, true)
- loginData.setSource(Constants.PREFERENCE_SHOW_GPLAY, true)
+ preferenceManagerModule.setSource(Constants.PREFERENCE_SHOW_FOSS, true)
+ preferenceManagerModule.setSource(Constants.PREFERENCE_SHOW_PWA, true)
+ preferenceManagerModule.setSource(Constants.PREFERENCE_SHOW_GPLAY, true)
}
}
diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginData.kt b/app/src/main/java/foundation/e/apps/data/login/LoginData.kt
deleted file mode 100644
index c9da999b5d1647f58b547111adcb4d44ac0a91dd..0000000000000000000000000000000000000000
--- a/app/src/main/java/foundation/e/apps/data/login/LoginData.kt
+++ /dev/null
@@ -1,188 +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 android.content.Context
-import androidx.datastore.preferences.core.edit
-import androidx.datastore.preferences.core.stringPreferencesKey
-import androidx.preference.PreferenceManager
-import com.aurora.gplayapi.data.models.AuthData
-import com.google.gson.Gson
-import dagger.hilt.android.qualifiers.ApplicationContext
-import foundation.e.apps.data.Constants.PREFERENCE_SHOW_FOSS
-import foundation.e.apps.data.Constants.PREFERENCE_SHOW_GPLAY
-import foundation.e.apps.data.Constants.PREFERENCE_SHOW_PWA
-import foundation.e.apps.data.enums.User
-import foundation.e.apps.data.preference.DataStoreModule.Companion.dataStore
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.runBlocking
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class LoginData @Inject constructor(
- @ApplicationContext
- private val context: Context,
- private val gson: Gson
-) {
-
- private val preferenceManager = PreferenceManager.getDefaultSharedPreferences(context)
-
- private val AUTHDATA = stringPreferencesKey("authData")
- private val EMAIL = stringPreferencesKey("email")
- private val OAUTHTOKEN = stringPreferencesKey("oauthtoken")
- private val AASTOKEN = stringPreferencesKey("aasToken")
- private val USERTYPE = stringPreferencesKey("userType")
-
- /*
- * Difference between OAUTHTOKEN and AASTOKEN:
- *
- * These two are used only for Google login, not for Anonymous login.
- * OAuthToken is obtained from the Google Login web page, from the cookies.
- * This OAuthToken is then used by AC2DMTask in GPlayAPIImpl class
- * to generate AasToken.
- *
- * To get Google Play Store data, we need to create an AuthData instance.
- * For Google user, this can only be done using AasToken, not OAuthToken.
- *
- * Very important: AasToken can be generated only ONCE from one OAuthToken.
- * We cannot get AasToken again from the same OAuthToken. Thus it is
- * important to safely store the AasToken to regenerate AuthData if needed.
- * If AasToken is not stored, user has to logout and login again.
- */
-
- private val authData = context.dataStore.data.map { it[AUTHDATA] ?: "" }
- private val emailData = context.dataStore.data.map { it[EMAIL] ?: "" }
- private val aasToken = context.dataStore.data.map { it[AASTOKEN] ?: "" }
- private val oauthToken = context.dataStore.data.map { it[OAUTHTOKEN] ?: "" }
- private val userType = context.dataStore.data.map { it[USERTYPE] ?: "" }
-
- // Setters
-
- suspend fun saveAuthData(authData: AuthData) {
- context.dataStore.edit {
- it[AUTHDATA] = gson.toJson(authData)
- }
- }
-
- suspend fun saveUserType(user: User) {
- context.dataStore.edit {
- it[USERTYPE] = user.name
- }
- }
-
- suspend fun saveGoogleLogin(email: String, token: String) {
- context.dataStore.edit {
- it[EMAIL] = email
- it[OAUTHTOKEN] = token
- }
- }
-
- suspend fun saveAasToken(aasToken: String) {
- context.dataStore.edit {
- it[AASTOKEN] = aasToken
- }
- }
-
- // Getters
-
- fun getAuthData(): String {
- return runBlocking {
- authData.first()
- }
- }
-
- /**
- * Get the [User] type stored in the data store.
- * In case nothing is stored, returns [User.UNAVAILABLE].
- *
- * No need to wrap this function in try-catch block.
- *
- * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5680
- */
- fun getUserType(): User {
- return runBlocking {
- userType.first().run {
- val userStrings = User.values().map { it.name }
- if (this !in userStrings) User.UNAVAILABLE
- else User.valueOf(this)
- }
- }
- }
-
- fun getEmail(): String {
- return runBlocking {
- emailData.first()
- }
- }
-
- fun getOAuthToken(): String {
- return runBlocking {
- oauthToken.first()
- }
- }
-
- fun getAASToken(): String {
- return runBlocking {
- aasToken.first()
- }
- }
-
- fun isOpenSourceSelected() = preferenceManager.getBoolean(PREFERENCE_SHOW_FOSS, true)
- fun isPWASelected() = preferenceManager.getBoolean(PREFERENCE_SHOW_PWA, true)
- fun isGplaySelected() = preferenceManager.getBoolean(PREFERENCE_SHOW_GPLAY, true)
-
- fun setSource(source: String, value: Boolean) {
- val editor = preferenceManager.edit()
- editor.run {
- this.putBoolean(source, value)
- }
- editor.apply()
- }
-
- // Clear data
-
- /**
- * Destroy auth credentials if they are no longer valid.
- *
- * Modification for issue: https://gitlab.e.foundation/e/backlog/-/issues/5168
- * Previously this method would also remove [USERTYPE].
- * To clear this value, call [clearUserType].
- */
- suspend fun destroyCredentials() {
- context.dataStore.edit {
- it.remove(AUTHDATA)
- it.remove(EMAIL)
- it.remove(OAUTHTOKEN)
- it.remove(AASTOKEN)
- }
- }
-
- suspend fun clearAuthData() {
- context.dataStore.edit {
- it.remove(AUTHDATA)
- }
- }
-
- suspend fun clearUserType() {
- context.dataStore.edit {
- it.remove(USERTYPE)
- }
- }
-}
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
index b408ff7f9bdd847bf30c5e532de6af3302ad3e99..5f303860428a073f759910e035cfe2d99c4f0a13 100644
--- a/app/src/main/java/foundation/e/apps/data/login/PlayStoreAuthenticator.kt
+++ b/app/src/main/java/foundation/e/apps/data/login/PlayStoreAuthenticator.kt
@@ -28,7 +28,10 @@ import foundation.e.apps.data.login.api.PlayStoreLoginManagerFactory
import foundation.e.apps.data.login.api.PlayStoreLoginManager
import foundation.e.apps.data.login.api.GoogleLoginManager
import foundation.e.apps.data.login.api.PlayStoreLoginWrapper
+import foundation.e.apps.data.preference.DataStoreModule
+import foundation.e.apps.data.preference.PreferenceManagerModule
import foundation.e.apps.data.retryWithBackoff
+import foundation.e.apps.data.preference.getSync
import timber.log.Timber
import java.util.Locale
import javax.inject.Inject
@@ -44,14 +47,15 @@ import javax.inject.Singleton
class PlayStoreAuthenticator @Inject constructor(
@ApplicationContext private val context: Context,
private val gson: Gson,
- private val loginData: LoginData,
+ private val dataStoreModule: DataStoreModule,
+ private val preferenceManagerModule: PreferenceManagerModule,
) : StoreAuthenticator, AuthDataValidator {
@Inject
lateinit var loginManagerFactory: PlayStoreLoginManagerFactory
private val user: User
- get() = loginData.getUserType()
+ get() = dataStoreModule.getUserType()
private val loginManager: PlayStoreLoginManager
get() = loginManagerFactory.createLoginManager(user)
@@ -69,7 +73,7 @@ class PlayStoreAuthenticator @Inject constructor(
*/
return false
}
- return loginData.isGplaySelected()
+ return preferenceManagerModule.isGplaySelected()
}
/**
@@ -106,7 +110,7 @@ class PlayStoreAuthenticator @Inject constructor(
}
override suspend fun logout() {
- loginData.clearAuthData()
+ dataStoreModule.saveAuthData(null)
}
/**
@@ -114,7 +118,7 @@ class PlayStoreAuthenticator @Inject constructor(
* Returns null if nothing is saved.
*/
private fun getSavedAuthData(): AuthData? {
- val authJson = loginData.getAuthData()
+ val authJson = dataStoreModule.authData.getSync()
return if (authJson.isBlank()) null
else try {
gson.fromJson(authJson, AuthData::class.java)
@@ -125,14 +129,14 @@ class PlayStoreAuthenticator @Inject constructor(
}
private suspend fun saveAuthData(authData: AuthData) {
- loginData.saveAuthData(authData)
+ dataStoreModule.saveAuthData(authData)
}
/**
* Generate new AuthData based on the user type.
*/
private suspend fun generateAuthData(): ResultSupreme {
- return when (loginData.getUserType()) {
+ return when (dataStoreModule.getUserType()) {
User.ANONYMOUS -> getAuthDataAnonymously()
User.GOOGLE -> getAuthDataWithGoogleAccount()
else -> ResultSupreme.Error("User type not ANONYMOUS or GOOGLE")
@@ -157,9 +161,9 @@ class PlayStoreAuthenticator @Inject constructor(
private suspend fun getAuthDataWithGoogleAccount(): ResultSupreme {
- val email = loginData.getEmail()
- val oauthToken = loginData.getOAuthToken()
- val aasToken = loginData.getAASToken()
+ val email = dataStoreModule.emailData.getSync()
+ val oauthToken = dataStoreModule.oauthToken.getSync()
+ val aasToken = dataStoreModule.aasToken.getSync()
/*
* If aasToken is not blank, means it was stored successfully from a previous Google login.
* Use it to fetch auth data.
@@ -195,7 +199,7 @@ class PlayStoreAuthenticator @Inject constructor(
/*
* Finally save the aasToken and create auth data.
*/
- loginData.saveAasToken(aasTokenFetched)
+ dataStoreModule.saveAasToken(aasTokenFetched)
return loginWrapper.login(locale).run {
if (isSuccess()) ResultSupreme.Success(formatAuthData(this.data!!))
else this
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
index c0d5eff3c09a2d7e2b6dcb27f0311f59b0feae23..d99fdb7f92a481a1ae67a6819fe175dc15ad4f58 100644
--- 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
@@ -23,7 +23,8 @@ 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.login.LoginData
+import foundation.e.apps.data.preference.DataStoreModule
+import foundation.e.apps.data.preference.getSync
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.Properties
@@ -32,7 +33,7 @@ class GoogleLoginManager(
private val gPlayHttpClient: GPlayHttpClient,
private val nativeDeviceProperty: Properties,
private val aC2DMTask: AC2DMTask,
- private val loginData: LoginData
+ private val dataStoreModule: DataStoreModule,
) : PlayStoreLoginManager {
/**
@@ -58,8 +59,8 @@ class GoogleLoginManager(
* @return authData: authentication data
*/
override suspend fun login(): AuthData? {
- val email = loginData.getEmail()
- val aasToken = loginData.getAASToken()
+ val email = dataStoreModule.emailData.getSync()
+ val aasToken = dataStoreModule.aasToken.getSync()
var authData: AuthData?
withContext(Dispatchers.IO) {
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
index 784532df940cbe9f4ec3daf96d71e0aef418730b..fc1c55156aaa747569a37d350591355f536e7a2e 100644
--- 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
@@ -21,7 +21,7 @@ import com.google.gson.Gson
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.login.LoginData
+import foundation.e.apps.data.preference.DataStoreModule
import java.util.Properties
import javax.inject.Inject
import javax.inject.Singleton
@@ -32,12 +32,12 @@ class PlayStoreLoginManagerFactory @Inject constructor(
private val nativeDeviceProperty: Properties,
private val aC2DMTask: AC2DMTask,
private val gson: Gson,
- private val loginData: LoginData
+ private val dataStoreModule: DataStoreModule,
) {
fun createLoginManager(user: User): PlayStoreLoginManager {
return when (user) {
- User.GOOGLE -> GoogleLoginManager(gPlayHttpClient, nativeDeviceProperty, aC2DMTask, loginData)
+ User.GOOGLE -> GoogleLoginManager(gPlayHttpClient, nativeDeviceProperty, aC2DMTask, dataStoreModule)
else -> AnonymousLoginManager(gPlayHttpClient, nativeDeviceProperty, gson)
}
}
diff --git a/app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt b/app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt
index 8bd36cbfe1db8bcdd163960a7b4a0891b6de5c44..240d0716cbdcd59db27d0fa321810be37cfc6b48 100644
--- a/app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt
+++ b/app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt
@@ -32,15 +32,11 @@ class DataStoreManager @Inject constructor() {
lateinit var gson: Gson
fun getAuthData(): AuthData {
- val authDataJson = dataStoreModule.getAuthDataSync()
+ val authDataJson = dataStoreModule.authData.getSync()
return gson.fromJson(authDataJson, AuthData::class.java) ?: AuthData("", "")
}
fun getUserType(): User {
return dataStoreModule.getUserType()
}
-
- fun getAuthDataJson(): String {
- return dataStoreModule.getAuthDataSync()
- }
}
diff --git a/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt b/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt
index a8482aaeba868baa93c459411a360322ecd19965..7f042d00debf807c317d683e49ba23751ff2ce9c 100644
--- a/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt
+++ b/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt
@@ -1,145 +1,156 @@
-/*
- * Apps Quickly and easily install Android apps onto your device!
- * 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.preference
-
-import android.content.Context
-import androidx.datastore.preferences.core.booleanPreferencesKey
-import androidx.datastore.preferences.core.edit
-import androidx.datastore.preferences.core.stringPreferencesKey
-import androidx.datastore.preferences.preferencesDataStore
-import com.aurora.gplayapi.data.models.AuthData
-import com.google.gson.Gson
-import dagger.hilt.android.qualifiers.ApplicationContext
-import foundation.e.apps.data.enums.User
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.runBlocking
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class DataStoreModule @Inject constructor(
- @ApplicationContext
- private val context: Context,
- private val gson: Gson
-) {
-
- companion object {
- private const val preferenceDataStoreName = "Settings"
- val Context.dataStore by preferencesDataStore(preferenceDataStoreName)
- }
-
- private val AUTHDATA = stringPreferencesKey("authData")
- private val EMAIL = stringPreferencesKey("email")
- private val OAUTHTOKEN = stringPreferencesKey("oauthtoken")
- private val USERTYPE = stringPreferencesKey("userType")
- private val TOCSTATUS = booleanPreferencesKey("tocStatus")
- private val TOSVERSION = stringPreferencesKey("tosversion")
-
- val authData = context.dataStore.data.map { it[AUTHDATA] ?: "" }
- val emailData = context.dataStore.data.map { it[EMAIL] ?: "" }
- val aasToken = context.dataStore.data.map { it[OAUTHTOKEN] ?: "" }
- val userType = context.dataStore.data.map { it[USERTYPE] ?: "" }
- val tocStatus = context.dataStore.data.map { it[TOCSTATUS] ?: false }
- val tosVersion = context.dataStore.data.map { it[TOSVERSION] ?: "" }
-
- /**
- * Allows to save gplay API token data into datastore
- */
- suspend fun saveCredentials(authData: AuthData) {
- context.dataStore.edit {
- it[AUTHDATA] = gson.toJson(authData)
- }
- }
-
- /**
- * Destroy auth credentials if they are no longer valid.
- *
- * Modification for issue: https://gitlab.e.foundation/e/backlog/-/issues/5168
- * Previously this method would also remove [USERTYPE].
- * To clear this value, call [clearUserType].
- */
- suspend fun destroyCredentials() {
- context.dataStore.edit {
- it.remove(AUTHDATA)
- it.remove(EMAIL)
- it.remove(OAUTHTOKEN)
- }
- }
-
- suspend fun clearUserType() {
- context.dataStore.edit {
- it.remove(USERTYPE)
- }
- }
-
- /**
- * TOC status
- */
- suspend fun saveTOCStatus(status: Boolean, tosVersion: String) {
- context.dataStore.edit {
- it[TOCSTATUS] = status
- it[TOSVERSION] = tosVersion
- }
- }
-
- fun getTOSVersion(): String {
- return runBlocking {
- tosVersion.first()
- }
- }
-
- /**
- * User auth type
- */
- suspend fun saveUserType(user: User) {
- context.dataStore.edit {
- it[USERTYPE] = user.name
- }
- }
-
- fun getAuthDataSync(): String {
- return runBlocking {
- authData.first()
- }
- }
-
- suspend fun saveEmail(email: String, token: String) {
- context.dataStore.edit {
- it[EMAIL] = email
- it[OAUTHTOKEN] = token
- }
- }
-
- fun getEmail(): String {
- return runBlocking {
- emailData.first()
- }
- }
-
- fun getUserType(): User {
- return runBlocking {
- userType.first().run {
- val userStrings = User.values().map { it.name }
- if (this !in userStrings) User.UNAVAILABLE
- else User.valueOf(this)
- }
- }
- }
-}
+/*
+ * Apps Quickly and easily install Android apps onto your device!
+ * 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.preference
+
+import android.content.Context
+import androidx.datastore.preferences.core.booleanPreferencesKey
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import com.aurora.gplayapi.data.models.AuthData
+import com.google.gson.Gson
+import dagger.hilt.android.qualifiers.ApplicationContext
+import foundation.e.apps.data.enums.User
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.runBlocking
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Difference between [OAUTHTOKEN] and [AASTOKEN]:
+ *
+ * These two are used only for Google login, not for Anonymous login.
+ * OAuthToken is obtained from the Google Login web page, from the cookies.
+ * This OAuthToken is then used by AC2DMTask in GPlayAPIImpl class
+ * to generate AasToken.
+ *
+ * To get Google Play Store data, we need to create an AuthData instance.
+ * For Google user, this can only be done using AasToken, not OAuthToken.
+ *
+ * Very important: AasToken can be generated only ONCE from one OAuthToken.
+ * We cannot get AasToken again from the same OAuthToken. Thus it is
+ * important to safely store the AasToken to regenerate AuthData if needed.
+ * If AasToken is not stored, user has to logout and login again.
+ */
+
+@Singleton
+class DataStoreModule @Inject constructor(
+ @ApplicationContext
+ private val context: Context,
+ private val gson: Gson
+) {
+
+ companion object {
+ private const val preferenceDataStoreName = "Settings"
+ val Context.dataStore by preferencesDataStore(preferenceDataStoreName)
+ }
+
+ private val AUTHDATA = stringPreferencesKey("authData")
+ private val EMAIL = stringPreferencesKey("email")
+ private val OAUTHTOKEN = stringPreferencesKey("oauthtoken")
+ private val AASTOKEN = stringPreferencesKey("aasToken")
+ private val USERTYPE = stringPreferencesKey("userType")
+ private val TOCSTATUS = booleanPreferencesKey("tocStatus")
+ private val TOSVERSION = stringPreferencesKey("tosversion")
+
+ val authData = 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] ?: "" }
+ val userType = context.dataStore.data.map { it[USERTYPE] ?: "" }
+ val tocStatus = context.dataStore.data.map { it[TOCSTATUS] ?: false }
+ val tosVersion = context.dataStore.data.map { it[TOSVERSION] ?: "" }
+
+ /**
+ * Allows to save gplay API token data into datastore
+ */
+ suspend fun saveAuthData(authData: AuthData?) {
+ context.dataStore.edit {
+ if (authData == null) it.remove(AUTHDATA)
+ else it[AUTHDATA] = gson.toJson(authData)
+ }
+ }
+
+ /**
+ * Destroy auth credentials if they are no longer valid.
+ *
+ * Modification for issue: https://gitlab.e.foundation/e/backlog/-/issues/5168
+ * Previously this method would also remove [USERTYPE].
+ * To clear this value, call [saveUserType] with null.
+ */
+ suspend fun destroyCredentials() {
+ context.dataStore.edit {
+ it.remove(AUTHDATA)
+ it.remove(EMAIL)
+ it.remove(OAUTHTOKEN)
+ it.remove(AASTOKEN)
+ }
+ }
+
+ /**
+ * TOC status
+ */
+ suspend fun saveTOCStatus(status: Boolean, tosVersion: String) {
+ context.dataStore.edit {
+ it[TOCSTATUS] = status
+ it[TOSVERSION] = tosVersion
+ }
+ }
+
+ /**
+ * User auth type
+ */
+ suspend fun saveUserType(user: User?) {
+ context.dataStore.edit {
+ if (user == null) it.remove(USERTYPE)
+ else it[USERTYPE] = user.name
+ }
+ }
+
+ fun getUserType(): User {
+ return runBlocking {
+ userType.first().run {
+ val userStrings = User.values().map { it.name }
+ if (this !in userStrings) User.UNAVAILABLE
+ else User.valueOf(this)
+ }
+ }
+ }
+
+ suspend fun saveAasToken(aasToken: String) {
+ context.dataStore.edit {
+ it[AASTOKEN] = aasToken
+ }
+ }
+
+ suspend fun saveGoogleLogin(email: String, token: String) {
+ context.dataStore.edit {
+ it[EMAIL] = email
+ it[OAUTHTOKEN] = token
+ }
+ }
+}
+
+fun Flow.getSync(): String {
+ return runBlocking {
+ this@getSync.first()
+ }
+}
diff --git a/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt b/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt
index 0dba23101785c5ca3d386dcd728fad1494b18466..813fa8584cb703ea1f8a0b0208479e5d0781bc2a 100644
--- a/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt
+++ b/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt
@@ -67,4 +67,12 @@ class PreferenceManagerModule @Inject constructor(
context.getString(R.string.update_apps_from_other_stores),
true
)
+
+ fun setSource(source: String, value: Boolean) {
+ val editor = preferenceManager.edit()
+ editor.run {
+ this.putBoolean(source, value)
+ }
+ editor.apply()
+ }
}
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 2e23ddc7be4a6125c8a297f2f857703ba117f64f..02b8842ff9217f7c9554d22d64bd21035e5cff89 100644
--- a/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt
+++ b/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt
@@ -24,6 +24,7 @@ import com.google.gson.Gson
import foundation.e.apps.data.Constants.ACTION_AUTHDATA_DUMP
import foundation.e.apps.data.Constants.TAG_AUTHDATA_DUMP
import foundation.e.apps.data.preference.DataStoreModule
+import foundation.e.apps.data.preference.getSync
import org.json.JSONObject
import timber.log.Timber
@@ -47,7 +48,7 @@ class DumpAuthData : BroadcastReceiver() {
private fun getAuthDataDump(context: Context): String {
val gson = Gson()
// TODO: replace with context.configuration
- val authData = DataStoreModule(context, gson).getAuthDataSync().let {
+ val authData = DataStoreModule(context, gson).authData.getSync().let {
gson.fromJson(it, AuthData::class.java)
}
val filteredData = JSONObject().apply {
diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt
index d110ba5c7eabc5477ac88bdbd893a8f05b8e0df6..51a52c96575a90ce2291a3c0e3728bac2a2bff65 100644
--- a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt
+++ b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt
@@ -49,6 +49,7 @@ import foundation.e.apps.data.preference.DataStoreModule
import foundation.e.apps.install.pkg.PWAManagerModule
import foundation.e.apps.install.pkg.PkgManagerModule
import foundation.e.apps.install.workmanager.AppInstallProcessor
+import foundation.e.apps.data.preference.getSync
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
@@ -91,7 +92,7 @@ class MainActivityViewModel @Inject constructor(
}
fun getUserEmail(): String {
- return dataStoreModule.getEmail()
+ return dataStoreModule.emailData.getSync()
}
fun uploadFaultyTokenToEcloud(email: String, description: String = "") {
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 5be760a1b3c7aa07e34bbc7531d1866227a65487..f659e91832f9704a493ee486bcef4857eb292f56 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
@@ -25,6 +25,7 @@ import com.aurora.gplayapi.data.models.AuthData
import com.google.gson.Gson
import dagger.hilt.android.AndroidEntryPoint
import foundation.e.apps.data.preference.DataStoreModule
+import foundation.e.apps.data.preference.getSync
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
@@ -52,10 +53,10 @@ class LocaleChangedBroadcastReceiver : BroadcastReceiver() {
}
GlobalScope.launch {
try {
- val authDataJson = dataStoreModule.getAuthDataSync()
+ val authDataJson = dataStoreModule.authData.getSync()
val authData = gson.fromJson(authDataJson, AuthData::class.java)
authData.locale = context.resources.configuration.locales[0]
- dataStoreModule.saveCredentials(authData)
+ dataStoreModule.saveAuthData(authData)
withContext(Dispatchers.IO) {
cache.evictAll()
}
diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInViewModel.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInViewModel.kt
index 61d7c57570176afe674c5337aeccec20f14e78e4..742cf51d9c723b77700af4c358ef05f6fbc6d0cd 100644
--- a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInViewModel.kt
+++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInViewModel.kt
@@ -21,18 +21,4 @@ class SignInViewModel @Inject constructor(
private val _authLiveData: MutableLiveData = MutableLiveData()
val authLiveData: LiveData = _authLiveData
- fun saveUserType(user: User) {
- viewModelScope.launch {
- dataStoreModule.saveUserType(user)
- if (user == User.UNAVAILABLE) {
- dataStoreModule.destroyCredentials()
- }
- }
- }
-
- fun saveEmailToken(email: String, token: String) {
- viewModelScope.launch {
- dataStoreModule.saveEmail(email, token)
- }
- }
}