Loading app/src/main/java/foundation/e/apps/di/LoginModule.kt 0 → 100644 +38 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.di import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import foundation.e.apps.login.LoginSourceCleanApk import foundation.e.apps.login.LoginSourceGPlay import foundation.e.apps.login.LoginSourceInterface @InstallIn(SingletonComponent::class) @Module object LoginModule { @Provides fun providesLoginSources( gPlay: LoginSourceGPlay, cleanApk: LoginSourceCleanApk, ): List<LoginSourceInterface> { return listOf(gPlay, cleanApk) } } app/src/main/java/foundation/e/apps/login/AuthObject.kt 0 → 100644 +42 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.login import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.api.ResultSupreme import foundation.e.apps.login.AuthObject.GPlayAuth import foundation.e.apps.utils.enums.User /** * Auth objects define which sources data is to be loaded from, for each source, also provides * a means of authentication to get the data. * Example, for Google Play, we have [GPlayAuth] which contains [AuthData]. * For CleanApk, we don't need any authentication. * * In future, combination of sources is possible. * * https://gitlab.e.foundation/e/backlog/-/issues/5680 */ sealed class AuthObject { abstract val result: ResultSupreme<*> class GPlayAuth(override val result: ResultSupreme<AuthData?>, val user: User) : AuthObject() class CleanApk(override val result: ResultSupreme<Unit>, val user: User) : AuthObject() // Add more auth types here } app/src/main/java/foundation/e/apps/login/LoginCommon.kt 0 → 100644 +61 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.login import foundation.e.apps.utils.enums.User 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 loginDataStore: LoginDataStore, ) { fun getAuthTypes(): List<String> { if (loginDataStore.getUserType() == User.UNAVAILABLE) { return emptyList() } return ArrayList<String>().apply { if (loginDataStore.isGplaySelected()) { add(AuthObject.GPlayAuth::class.java.simpleName) } if (loginDataStore.isPWASelected() || loginDataStore.isOpenSourceSelected()) { add(AuthObject.CleanApk::class.java.simpleName) } } } suspend fun saveUserType(user: User) { loginDataStore.saveUserType(user) } suspend fun saveGoogleLogin(email: String, oauth: String) { loginDataStore.saveGoogleLogin(email, oauth) } suspend fun logout() { loginDataStore.destroyCredentials() loginDataStore.clearUserType() } } app/src/main/java/foundation/e/apps/login/LoginDataStore.kt 0 → 100644 +183 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.login import android.content.Context import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore 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.utils.Constants.PREFERENCE_SHOW_FOSS import foundation.e.apps.utils.Constants.PREFERENCE_SHOW_GPLAY import foundation.e.apps.utils.Constants.PREFERENCE_SHOW_PWA import foundation.e.apps.utils.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 LoginDataStore @Inject constructor( @ApplicationContext private val context: Context, private val gson: Gson ) { private val preferenceDataStoreName = "Settings" private val Context.dataStore by preferencesDataStore(preferenceDataStoreName) 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. */ val authData = context.dataStore.data.map { it[AUTHDATA] ?: "" } val emailData = context.dataStore.data.map { it[EMAIL] ?: "" } val aasToken = context.dataStore.data.map { it[AASTOKEN] ?: "" } val oauthToken = context.dataStore.data.map { it[OAUTHTOKEN] ?: "" } 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) // 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) } } } app/src/main/java/foundation/e/apps/login/LoginSourceCleanApk.kt 0 → 100644 +45 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.login import foundation.e.apps.api.ResultSupreme 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 LoginSourceCleanApk @Inject constructor( val loginDataStore: LoginDataStore, ) : LoginSourceInterface { override fun isActive(): Boolean { return loginDataStore.isOpenSourceSelected() || loginDataStore.isPWASelected() } override suspend fun getAuthObject(): AuthObject.CleanApk { return AuthObject.CleanApk( ResultSupreme.Success(Unit), loginDataStore.getUserType(), ) } override suspend fun clearSavedAuth() {} } Loading
app/src/main/java/foundation/e/apps/di/LoginModule.kt 0 → 100644 +38 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.di import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import foundation.e.apps.login.LoginSourceCleanApk import foundation.e.apps.login.LoginSourceGPlay import foundation.e.apps.login.LoginSourceInterface @InstallIn(SingletonComponent::class) @Module object LoginModule { @Provides fun providesLoginSources( gPlay: LoginSourceGPlay, cleanApk: LoginSourceCleanApk, ): List<LoginSourceInterface> { return listOf(gPlay, cleanApk) } }
app/src/main/java/foundation/e/apps/login/AuthObject.kt 0 → 100644 +42 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.login import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.api.ResultSupreme import foundation.e.apps.login.AuthObject.GPlayAuth import foundation.e.apps.utils.enums.User /** * Auth objects define which sources data is to be loaded from, for each source, also provides * a means of authentication to get the data. * Example, for Google Play, we have [GPlayAuth] which contains [AuthData]. * For CleanApk, we don't need any authentication. * * In future, combination of sources is possible. * * https://gitlab.e.foundation/e/backlog/-/issues/5680 */ sealed class AuthObject { abstract val result: ResultSupreme<*> class GPlayAuth(override val result: ResultSupreme<AuthData?>, val user: User) : AuthObject() class CleanApk(override val result: ResultSupreme<Unit>, val user: User) : AuthObject() // Add more auth types here }
app/src/main/java/foundation/e/apps/login/LoginCommon.kt 0 → 100644 +61 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.login import foundation.e.apps.utils.enums.User 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 loginDataStore: LoginDataStore, ) { fun getAuthTypes(): List<String> { if (loginDataStore.getUserType() == User.UNAVAILABLE) { return emptyList() } return ArrayList<String>().apply { if (loginDataStore.isGplaySelected()) { add(AuthObject.GPlayAuth::class.java.simpleName) } if (loginDataStore.isPWASelected() || loginDataStore.isOpenSourceSelected()) { add(AuthObject.CleanApk::class.java.simpleName) } } } suspend fun saveUserType(user: User) { loginDataStore.saveUserType(user) } suspend fun saveGoogleLogin(email: String, oauth: String) { loginDataStore.saveGoogleLogin(email, oauth) } suspend fun logout() { loginDataStore.destroyCredentials() loginDataStore.clearUserType() } }
app/src/main/java/foundation/e/apps/login/LoginDataStore.kt 0 → 100644 +183 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.login import android.content.Context import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore 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.utils.Constants.PREFERENCE_SHOW_FOSS import foundation.e.apps.utils.Constants.PREFERENCE_SHOW_GPLAY import foundation.e.apps.utils.Constants.PREFERENCE_SHOW_PWA import foundation.e.apps.utils.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 LoginDataStore @Inject constructor( @ApplicationContext private val context: Context, private val gson: Gson ) { private val preferenceDataStoreName = "Settings" private val Context.dataStore by preferencesDataStore(preferenceDataStoreName) 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. */ val authData = context.dataStore.data.map { it[AUTHDATA] ?: "" } val emailData = context.dataStore.data.map { it[EMAIL] ?: "" } val aasToken = context.dataStore.data.map { it[AASTOKEN] ?: "" } val oauthToken = context.dataStore.data.map { it[OAUTHTOKEN] ?: "" } 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) // 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) } } }
app/src/main/java/foundation/e/apps/login/LoginSourceCleanApk.kt 0 → 100644 +45 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.apps.login import foundation.e.apps.api.ResultSupreme 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 LoginSourceCleanApk @Inject constructor( val loginDataStore: LoginDataStore, ) : LoginSourceInterface { override fun isActive(): Boolean { return loginDataStore.isOpenSourceSelected() || loginDataStore.isPWASelected() } override suspend fun getAuthObject(): AuthObject.CleanApk { return AuthObject.CleanApk( ResultSupreme.Success(Unit), loginDataStore.getUserType(), ) } override suspend fun clearSavedAuth() {} }