Migrate account settings
This commit is contained in:
parent
0902ee2583
commit
5b9ce4d4ae
1
accounts/.gitignore
vendored
Normal file
1
accounts/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
||||
48
accounts/build.gradle.kts
Normal file
48
accounts/build.gradle.kts
Normal file
@ -0,0 +1,48 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("kotlin-android")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = sdk.versions.compileSdk.get().toInt()
|
||||
|
||||
defaultConfig {
|
||||
minSdk = sdk.versions.minSdk.get().toInt()
|
||||
targetSdk = sdk.versions.targetSdk.get().toInt()
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.bundles.kotlin)
|
||||
implementation(libs.androidx.core)
|
||||
|
||||
implementation(libs.koin.android)
|
||||
|
||||
implementation(project(":g-services"))
|
||||
implementation(project(":ms-services"))
|
||||
implementation(project(":owncloud"))
|
||||
implementation(project(":nextcloud"))
|
||||
|
||||
}
|
||||
0
accounts/consumer-rules.pro
Normal file
0
accounts/consumer-rules.pro
Normal file
21
accounts/proguard-rules.pro
vendored
Normal file
21
accounts/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
5
accounts/src/main/AndroidManifest.xml
Normal file
5
accounts/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="de.mm20.launcher2.accounts">
|
||||
|
||||
</manifest>
|
||||
@ -0,0 +1,6 @@
|
||||
package de.mm20.launcher2.accounts
|
||||
|
||||
data class Account(
|
||||
val userName: String,
|
||||
val type: AccountType,
|
||||
)
|
||||
@ -0,0 +1,8 @@
|
||||
package de.mm20.launcher2.accounts
|
||||
|
||||
enum class AccountType {
|
||||
Google,
|
||||
Microsoft,
|
||||
Nextcloud,
|
||||
Owncloud,
|
||||
}
|
||||
@ -0,0 +1,132 @@
|
||||
package de.mm20.launcher2.accounts
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import de.mm20.launcher2.gservices.GoogleApiHelper
|
||||
import de.mm20.launcher2.msservices.MicrosoftGraphApiHelper
|
||||
import de.mm20.launcher2.nextcloud.NextcloudApiHelper
|
||||
import de.mm20.launcher2.owncloud.OwncloudClient
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
interface AccountsRepository {
|
||||
fun signin(context: Activity, accountType: AccountType)
|
||||
fun signout(accountType: AccountType)
|
||||
|
||||
/**
|
||||
* Whether support for this account type is enabled in this build
|
||||
*/
|
||||
fun isSupported(accountType: AccountType): Boolean
|
||||
|
||||
suspend fun getCurrentlySignedInAccount(accountType: AccountType): Account?
|
||||
}
|
||||
|
||||
internal class AccountsRepositoryImpl(
|
||||
private val context: Context
|
||||
) : AccountsRepository {
|
||||
private val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||
|
||||
private val googleApiHelper = GoogleApiHelper.getInstance(context)
|
||||
private val msGraphApiHelper = MicrosoftGraphApiHelper.getInstance(context)
|
||||
private val nextcloudApiHelper = NextcloudApiHelper(context)
|
||||
private val owncloudApiHelper = OwncloudClient(context)
|
||||
|
||||
override fun signin(context: Activity, accountType: AccountType) {
|
||||
when (accountType) {
|
||||
AccountType.Google -> {
|
||||
scope.launch {
|
||||
googleApiHelper.login(context)
|
||||
}
|
||||
}
|
||||
AccountType.Microsoft -> {
|
||||
scope.launch {
|
||||
msGraphApiHelper.login(context)
|
||||
}
|
||||
}
|
||||
AccountType.Nextcloud ->
|
||||
scope.launch {
|
||||
nextcloudApiHelper.login(context)
|
||||
}
|
||||
AccountType.Owncloud ->
|
||||
scope.launch {
|
||||
owncloudApiHelper.login(context, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun signout(accountType: AccountType) {
|
||||
when (accountType) {
|
||||
AccountType.Google -> {
|
||||
googleApiHelper.logout()
|
||||
}
|
||||
AccountType.Microsoft -> {
|
||||
scope.launch {
|
||||
msGraphApiHelper.logout()
|
||||
}
|
||||
}
|
||||
AccountType.Nextcloud -> {
|
||||
scope.launch {
|
||||
nextcloudApiHelper.logout()
|
||||
}
|
||||
}
|
||||
AccountType.Owncloud -> {
|
||||
owncloudApiHelper.logout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSupported(accountType: AccountType): Boolean {
|
||||
return when (accountType) {
|
||||
AccountType.Google -> googleApiHelper.isAvailable()
|
||||
AccountType.Microsoft -> msGraphApiHelper.isAvailable()
|
||||
AccountType.Nextcloud -> true
|
||||
AccountType.Owncloud -> true
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getCurrentlySignedInAccount(accountType: AccountType): Account? {
|
||||
return when (accountType) {
|
||||
AccountType.Google -> {
|
||||
getGoogleAccount()
|
||||
}
|
||||
AccountType.Microsoft -> {
|
||||
getMicrosoftAccount()
|
||||
}
|
||||
AccountType.Nextcloud -> {
|
||||
getNextcloudAccount()
|
||||
}
|
||||
AccountType.Owncloud -> {
|
||||
getOwncloudAccount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getGoogleAccount(): Account? {
|
||||
return googleApiHelper.getAccount()?.let {
|
||||
Account(it.name, AccountType.Google)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getMicrosoftAccount(): Account? {
|
||||
return msGraphApiHelper.getUser()?.let {
|
||||
Account(it.name, AccountType.Microsoft)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getNextcloudAccount(): Account? {
|
||||
return nextcloudApiHelper.getLoggedInUser()?.let {
|
||||
Account(it.displayName, AccountType.Nextcloud)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getOwncloudAccount(): Account? {
|
||||
return owncloudApiHelper.getLoggedInUser()?.let {
|
||||
Account(it.displayName, AccountType.Owncloud)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package de.mm20.launcher2.accounts
|
||||
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.dsl.module
|
||||
|
||||
val accountsModule = module {
|
||||
factory<AccountsRepository> { AccountsRepositoryImpl(androidContext()) }
|
||||
}
|
||||
@ -114,6 +114,7 @@ dependencies {
|
||||
|
||||
implementation(libs.koin.android)
|
||||
|
||||
implementation(project(":accounts"))
|
||||
implementation(project(":applications"))
|
||||
implementation(project(":badges"))
|
||||
implementation(project(":base"))
|
||||
|
||||
@ -2,6 +2,7 @@ package de.mm20.launcher2
|
||||
|
||||
import android.app.Application
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import de.mm20.launcher2.accounts.accountsModule
|
||||
import de.mm20.launcher2.applications.applicationsModule
|
||||
import de.mm20.launcher2.badges.badgesModule
|
||||
import de.mm20.launcher2.calculator.calculatorModule
|
||||
@ -58,6 +59,7 @@ class LauncherApplication : Application(), CoroutineScope {
|
||||
androidContext(this@LauncherApplication)
|
||||
modules(
|
||||
listOf(
|
||||
accountsModule,
|
||||
applicationsModule,
|
||||
calculatorModule,
|
||||
badgesModule,
|
||||
|
||||
@ -8,7 +8,7 @@ buildscript {
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.1.0-alpha03")
|
||||
classpath(libs.kotlin.gradle)
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.30")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import com.microsoft.identity.client.AuthenticationCallback
|
||||
import com.microsoft.identity.client.IAuthenticationResult
|
||||
import com.microsoft.identity.client.ISingleAccountPublicClientApplication
|
||||
import com.microsoft.identity.client.PublicClientApplication
|
||||
import com.microsoft.identity.client.exception.MsalClientException
|
||||
import com.microsoft.identity.client.exception.MsalException
|
||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||
import de.mm20.launcher2.preferences.LauncherPreferences
|
||||
@ -76,7 +77,6 @@ class MicrosoftGraphApiHelper(val context: Context) {
|
||||
clientApplication.signIn(context, "", SCOPES, object : AuthenticationCallback {
|
||||
override fun onSuccess(authenticationResult: IAuthenticationResult?) {
|
||||
accessToken = authenticationResult?.accessToken
|
||||
LauncherPreferences.instance.searchOneDrive = true
|
||||
it.resume(authenticationResult)
|
||||
}
|
||||
|
||||
@ -96,11 +96,16 @@ class MicrosoftGraphApiHelper(val context: Context) {
|
||||
|
||||
suspend fun logout() {
|
||||
accessToken = null
|
||||
LauncherPreferences.instance.searchOneDrive = false
|
||||
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit {
|
||||
putString(PREF_ACCOUNT_NAME, null)
|
||||
}
|
||||
withContext(Dispatchers.IO) { getClientApplication()?.signOut() }
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
getClientApplication()?.signOut()
|
||||
} catch (e: MsalClientException) {
|
||||
CrashReporter.logException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getUser(): MsUser? {
|
||||
|
||||
@ -394,3 +394,4 @@ dependencyResolutionManagement {
|
||||
}
|
||||
}
|
||||
include(":notifications")
|
||||
include(":accounts")
|
||||
|
||||
@ -132,5 +132,6 @@ dependencies {
|
||||
implementation(project(":g-services"))
|
||||
implementation(project(":ms-services"))
|
||||
implementation(project(":owncloud"))
|
||||
implementation(project(":accounts"))
|
||||
|
||||
}
|
||||
@ -31,6 +31,7 @@ import de.mm20.launcher2.ui.settings.license.LicenseScreen
|
||||
import de.mm20.launcher2.ui.settings.main.MainSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.musicwidget.MusicWidgetSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.search.SearchSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.accounts.AccountsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.weatherwidget.WeatherWidgetSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.widgets.WidgetsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.wikipedia.WikipediaSettingsScreen
|
||||
@ -113,6 +114,9 @@ class SettingsActivity : BaseActivity() {
|
||||
composable("settings/badges") {
|
||||
BadgeSettingsScreen()
|
||||
}
|
||||
composable("settings/accounts") {
|
||||
AccountsSettingsScreen()
|
||||
}
|
||||
composable("settings/about") {
|
||||
AboutSettingsScreen()
|
||||
}
|
||||
|
||||
@ -0,0 +1,143 @@
|
||||
package de.mm20.launcher2.ui.settings.accounts
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.LinearProgressIndicator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.accounts.AccountType
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
|
||||
@Composable
|
||||
fun AccountsSettingsScreen() {
|
||||
val viewModel: AccountsSettingsScreenVM = viewModel()
|
||||
val context = LocalContext.current
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
|
||||
LaunchedEffect(null) {
|
||||
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
viewModel.onResume()
|
||||
}
|
||||
}
|
||||
|
||||
val loading by viewModel.loading.observeAsState(true)
|
||||
|
||||
PreferenceScreen(title = stringResource(R.string.preference_screen_services)) {
|
||||
if (loading) {
|
||||
item {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
return@PreferenceScreen
|
||||
}
|
||||
item {
|
||||
PreferenceCategory(title = stringResource(R.string.preference_category_services_nextcloud)) {
|
||||
val account by viewModel.nextcloudUser.observeAsState()
|
||||
Preference(
|
||||
title = if (account != null) {
|
||||
stringResource(R.string.preference_signin_logout)
|
||||
} else {
|
||||
stringResource(R.string.preference_nextcloud_signin)
|
||||
},
|
||||
summary = account?.let {
|
||||
stringResource(R.string.preference_signin_user, it.userName)
|
||||
} ?: stringResource(R.string.preference_nextcloud_signin_summary),
|
||||
onClick = {
|
||||
if (account != null) {
|
||||
viewModel.signOut(AccountType.Nextcloud)
|
||||
} else {
|
||||
viewModel.signIn(context as AppCompatActivity, AccountType.Nextcloud)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
PreferenceCategory(title = stringResource(R.string.preference_category_services_owncloud)) {
|
||||
val account by viewModel.owncloudUser.observeAsState()
|
||||
Preference(
|
||||
title = if (account != null) {
|
||||
stringResource(R.string.preference_signin_logout)
|
||||
} else {
|
||||
stringResource(R.string.preference_owncloud_signin)
|
||||
},
|
||||
summary = account?.let {
|
||||
stringResource(R.string.preference_signin_user, it.userName)
|
||||
} ?: stringResource(R.string.preference_owncloud_signin_summary),
|
||||
onClick = {
|
||||
if (account != null) {
|
||||
viewModel.signOut(AccountType.Owncloud)
|
||||
} else {
|
||||
viewModel.signIn(context as AppCompatActivity, AccountType.Owncloud)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
if (viewModel.isMicrosoftAvailable) {
|
||||
item {
|
||||
PreferenceCategory(title = stringResource(R.string.preference_category_services_microsoft)) {
|
||||
val account by viewModel.msUser.observeAsState()
|
||||
Preference(
|
||||
title = if (account != null) {
|
||||
stringResource(R.string.preference_signin_logout)
|
||||
} else {
|
||||
stringResource(R.string.preference_ms_signin)
|
||||
},
|
||||
summary = account?.let {
|
||||
stringResource(R.string.preference_signin_user, it.userName)
|
||||
} ?: stringResource(R.string.preference_ms_signin_summary),
|
||||
onClick = {
|
||||
if (account != null) {
|
||||
viewModel.signOut(AccountType.Microsoft)
|
||||
} else {
|
||||
viewModel.signIn(context as AppCompatActivity, AccountType.Microsoft)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (viewModel.isGoogleAvailable) {
|
||||
item {
|
||||
PreferenceCategory(title = stringResource(R.string.preference_category_services_google)) {
|
||||
val account by viewModel.googleUser.observeAsState()
|
||||
Preference(
|
||||
title = if (account != null) {
|
||||
stringResource(R.string.preference_signin_logout)
|
||||
} else {
|
||||
stringResource(R.string.preference_google_signin)
|
||||
},
|
||||
summary = account?.let {
|
||||
stringResource(R.string.preference_signin_user, it.userName)
|
||||
} ?: stringResource(R.string.preference_google_signin_summary),
|
||||
onClick = {
|
||||
if (account != null) {
|
||||
viewModel.signOut(AccountType.Google)
|
||||
} else {
|
||||
viewModel.signIn(context as AppCompatActivity, AccountType.Google)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package de.mm20.launcher2.ui.settings.accounts
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.accounts.Account
|
||||
import de.mm20.launcher2.accounts.AccountType
|
||||
import de.mm20.launcher2.accounts.AccountsRepository
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class AccountsSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val accountsRepository: AccountsRepository by inject()
|
||||
|
||||
val isGoogleAvailable = accountsRepository.isSupported(AccountType.Google)
|
||||
val isMicrosoftAvailable = accountsRepository.isSupported(AccountType.Microsoft)
|
||||
|
||||
val googleUser = MutableLiveData<Account?>(null)
|
||||
val msUser= MutableLiveData<Account?>(null)
|
||||
val nextcloudUser = MutableLiveData<Account?>(null)
|
||||
val owncloudUser = MutableLiveData<Account?>(null)
|
||||
|
||||
val loading = MutableLiveData(true)
|
||||
|
||||
fun onResume() {
|
||||
viewModelScope.launch {
|
||||
loading.value = true
|
||||
googleUser.value = accountsRepository.getCurrentlySignedInAccount(AccountType.Google)
|
||||
nextcloudUser.value = accountsRepository.getCurrentlySignedInAccount(AccountType.Nextcloud)
|
||||
msUser.value = accountsRepository.getCurrentlySignedInAccount(AccountType.Microsoft)
|
||||
owncloudUser.value = accountsRepository.getCurrentlySignedInAccount(AccountType.Owncloud)
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
fun signIn(activity: AppCompatActivity, accountType: AccountType) {
|
||||
accountsRepository.signin(activity, accountType)
|
||||
}
|
||||
|
||||
fun signOut(accountType: AccountType) {
|
||||
accountsRepository.signout(accountType)
|
||||
when(accountType){
|
||||
AccountType.Google -> googleUser.value = null
|
||||
AccountType.Microsoft -> msUser.value = null
|
||||
AccountType.Nextcloud -> nextcloudUser.value = null
|
||||
AccountType.Owncloud -> owncloudUser.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
package de.mm20.launcher2.ui.settings.services
|
||||
|
||||
class ServicesSettingsScreenVM {
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user