diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6c74facedea67ae2e092b4ac27472734a418574b..8af528d9931dee02c336edab99cc50ea73d94ddc 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,6 +9,9 @@ before_script:
- echo MURENA_CLIENT_ID=$MURENA_CLIENT_ID >> local.properties
- echo MURENA_CLIENT_SECRET=$MURENA_CLIENT_SECRET >> local.properties
- echo MURENA_REDIRECT_URI=$MURENA_REDIRECT_URI >> local.properties
+ - echo MURENA_LOGOUT_REDIRECT_URI=$MURENA_LOGOUT_REDIRECT_URI >> local.properties
+ - echo MURENA_BASE_URL=$MURENA_BASE_URL >> local.properties
+ - echo MURENA_DISCOVERY_END_POINT=$MURENA_DISCOVERY_END_POINT >> local.properties
- echo GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID >> local.properties
- echo GOOGLE_REDIRECT_URI=$GOOGLE_REDIRECT_URI >> local.properties
- echo YAHOO_CLIENT_ID=$YAHOO_CLIENT_ID >> local.properties
diff --git a/app/build.gradle b/app/build.gradle
index a2e14d695c52cd273d60259a933ab60b4e3be0f0..5ea75c8c48f155178c1175b0778cbc67d0cced02 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -111,6 +111,9 @@ android {
buildConfigField "String", "MURENA_CLIENT_ID", "\"${retrieveKey("MURENA_CLIENT_ID")}\""
buildConfigField "String", "MURENA_CLIENT_SECRET", "\"${retrieveKey("MURENA_CLIENT_SECRET")}\""
buildConfigField "String", "MURENA_REDIRECT_URI", "\"${retrieveKey("MURENA_REDIRECT_URI")}\""
+ buildConfigField "String", "MURENA_LOGOUT_REDIRECT_URI", "\"${retrieveKey("MURENA_LOGOUT_REDIRECT_URI")}\""
+ buildConfigField "String", "MURENA_BASE_URL", "\"${retrieveKey("MURENA_BASE_URL")}\""
+ buildConfigField "String", "MURENA_DISCOVERY_END_POINT", "\"${retrieveKey("MURENA_DISCOVERY_END_POINT")}\""
buildConfigField "String", "GOOGLE_CLIENT_ID", "\"${retrieveKey("GOOGLE_CLIENT_ID")}\""
buildConfigField "String", "GOOGLE_REDIRECT_URI", "\"${retrieveKey("GOOGLE_REDIRECT_URI")}\""
@@ -120,7 +123,9 @@ android {
manifestPlaceholders = [
'appAuthRedirectScheme': applicationId,
"googleAuthRedirectScheme": retrieveKey("GOOGLE_REDIRECT_URI"),
- "murenaAuthRedirectScheme": retrieveKey("MURENA_REDIRECT_URI")
+ "murenaAuthRedirectScheme": retrieveKey("MURENA_REDIRECT_URI"),
+
+ "murenaAuthLogoutRedirectScheme":retrieveKey("MURENA_LOGOUT_REDIRECT_URI")
]
}
@@ -219,7 +224,7 @@ dependencies {
implementation "commons-httpclient:commons-httpclient:3.1@jar" // remove after entire switch to lib v2
implementation 'org.apache.jackrabbit:jackrabbit-webdav:2.13.5' // remove after entire switch to lib v2
implementation 'com.google.code.gson:gson:2.10.1'
- implementation("foundation.e:Nextcloud-Android-Library:1.0.6-release") {
+ implementation("foundation.e:Nextcloud-Android-Library:1.0.7-u2.17-release") {
exclude group: 'com.gitlab.bitfireAT', module: 'dav4jvm'
exclude group: 'org.ogce', module: 'xpp3' // unused in Android and brings wrong Junit version
exclude group: 'com.squareup.okhttp3'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2039cf52d24c53aa27e9e88d1ee273223b1e927c..30e04639bf227a9af7c83a4dbc1829bca94a65c0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,6 +14,7 @@
+
@@ -682,6 +683,17 @@
+
+
+
+
+
+
+
+
+
+
MURENA
+ context.getString(R.string.google_account_type) -> GOOGLE
+ context.getString(R.string.yahoo_account_type) -> YAHOO
+ else -> null
+ }
+ }
+ }
+
private val mDiscoveryEndpoint: Uri?
private val mAuthEndpoint: Uri?
private val mTokenEndpoint: Uri?
@@ -71,8 +92,10 @@ enum class IdentityProvider(
val clientId: String
val clientSecret: String?
val redirectUri: Uri
+ val logoutRedirectUri: Uri?
val scope: String
val userInfoEndpoint: String?
+ val baseUrl: String?
init {
require(
@@ -87,8 +110,10 @@ enum class IdentityProvider(
this.clientSecret = clientSecret
this.redirectUri =
retrieveUri(redirectUri) ?: throw IllegalArgumentException("invalid redirect uri")
+ this.logoutRedirectUri = retrieveUri(logoutRedirectUri)
this.scope = scope
this.userInfoEndpoint = userInfoEndpoint
+ this.baseUrl = baseUrl
}
fun retrieveConfig(callback: RetrieveConfigurationCallback) {
@@ -105,4 +130,4 @@ enum class IdentityProvider(
null
} else Uri.parse(value)
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/kotlin/at/bitfire/davdroid/receiver/AccountRemovedReceiver.kt b/app/src/main/kotlin/at/bitfire/davdroid/receiver/AccountRemovedReceiver.kt
index 12be4afe5b058fa39bd19b96607ac40e240470c7..7409c058da21d434203d35a4fbeacb3d433c14c1 100644
--- a/app/src/main/kotlin/at/bitfire/davdroid/receiver/AccountRemovedReceiver.kt
+++ b/app/src/main/kotlin/at/bitfire/davdroid/receiver/AccountRemovedReceiver.kt
@@ -20,17 +20,16 @@ import android.accounts.AccountManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
+import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.syncadapter.AccountUtils
+import at.bitfire.davdroid.ui.signout.OpenIdEndSessionActivity
+import at.bitfire.davdroid.util.AuthStatePrefUtils
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
class AccountRemovedReceiver : BroadcastReceiver() {
- companion object {
- private const val ACCOUNT_REMOVAL_ACTION = "android.accounts.action.ACCOUNT_REMOVED"
- }
-
override fun onReceive(context: Context?, intent: Intent?) {
- if (context == null || intent == null || intent.action != ACCOUNT_REMOVAL_ACTION) {
+ if (context == null || intent == null || intent.action != AccountManager.ACTION_ACCOUNT_REMOVED) {
return
}
@@ -38,6 +37,43 @@ class AccountRemovedReceiver : BroadcastReceiver() {
val ownCloudClientManager = OwnCloudClientManagerFactory.getDefaultSingleton()
ownCloudClientManager.removeClientForByName(accountName)
+
+ clearOidcSession(
+ intent = intent,
+ context = context,
+ accountName = accountName
+ )
+ }
+
+ private fun clearOidcSession(
+ intent: Intent,
+ context: Context,
+ accountName: String
+ ) {
+ intent.extras?.getString(AccountManager.KEY_ACCOUNT_TYPE)?.let { type ->
+ val authStateString =
+ AuthStatePrefUtils.loadAuthState(context, accountName, type) ?: return
+ startOidcEndSessionActivity(
+ context = context,
+ authState = authStateString,
+ accountType = type
+ )
+ }
+ }
+
+ private fun startOidcEndSessionActivity(
+ context: Context,
+ authState: String,
+ accountType: String
+ ) {
+ val intent = Intent(context, OpenIdEndSessionActivity::class.java)
+ intent.apply {
+ putExtra(AccountSettings.KEY_AUTH_STATE, authState)
+ putExtra(AccountManager.KEY_ACCOUNT_TYPE, accountType)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+
+ context.applicationContext.startActivity(intent)
}
private fun getAccountName(context: Context, intent: Intent): String? {
diff --git a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt
index a9ef5bb507d563b7bfccab848552baf698c32a9b..39dee0e6b47b695cba3bdd2b8056c3e756234d88 100644
--- a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt
+++ b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt
@@ -19,6 +19,7 @@ import at.bitfire.davdroid.resource.LocalAddressBook
import at.bitfire.davdroid.syncadapter.AccountUtils
import at.bitfire.davdroid.syncadapter.PeriodicSyncWorker
import at.bitfire.davdroid.syncadapter.SyncUtils
+import at.bitfire.davdroid.util.AuthStatePrefUtils
import at.bitfire.davdroid.util.SsoUtils
import at.bitfire.davdroid.util.setAndVerifyUserData
import at.bitfire.ical4android.TaskProvider
@@ -118,8 +119,8 @@ class AccountSettings(
const val CONTACTS_APP_INTERACTION = "z-app-generated--contactsinteraction--recent/"
- const val COOKIE_KEY = "cookie_key"
- const val COOKIE_SEPARATOR = ""
+ const val COOKIE_KEY = NCAccountUtils.Constants.KEY_OKHTTP_COOKIES
+ const val COOKIE_SEPARATOR = NCAccountUtils.Constants.OKHTTP_COOKIE_SEPARATOR
/** Static property to indicate whether AccountSettings migration is currently running.
* **Access must be `synchronized` with `AccountSettings::class.java`.** */
@@ -271,6 +272,8 @@ class AccountSettings(
// OAuth
accountManager.setAndVerifyUserData(account, KEY_AUTH_STATE, credentials.authState?.jsonSerializeString())
accountManager.setAndVerifyUserData(account, KEY_CLIENT_SECRET, credentials.clientSecret)
+
+ AuthStatePrefUtils.saveAuthState(context, account, credentials.authState?.jsonSerializeString())
}
diff --git a/app/src/main/kotlin/at/bitfire/davdroid/syncadapter/DefaultAccountAuthenticatorService.kt b/app/src/main/kotlin/at/bitfire/davdroid/syncadapter/DefaultAccountAuthenticatorService.kt
index cc19d2dfa086f3be63a4e23adfeb1f0cc8c893bd..da635b072b8bed3064dbdc3864a779fbbe13d8fd 100644
--- a/app/src/main/kotlin/at/bitfire/davdroid/syncadapter/DefaultAccountAuthenticatorService.kt
+++ b/app/src/main/kotlin/at/bitfire/davdroid/syncadapter/DefaultAccountAuthenticatorService.kt
@@ -32,6 +32,7 @@ import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.LocalAddressBook
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.ui.setup.LoginActivity
+import at.bitfire.davdroid.util.AuthStatePrefUtils
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
@@ -205,7 +206,9 @@ abstract class DefaultAccountAuthenticatorService : Service(), OnAccountsUpdateL
val clientSecretString = accountManager.getUserData(account, AccountSettings.KEY_CLIENT_SECRET)
val clientSecret = OpenIdUtils.getClientAuthentication(clientSecretString)
- AuthorizationService(context).performTokenRequest(
+ val authorizationService = AuthorizationService(context)
+
+ authorizationService.performTokenRequest(
tokenRequest,
clientSecret
) { tokenResponse, ex ->
@@ -220,11 +223,16 @@ abstract class DefaultAccountAuthenticatorService : Service(), OnAccountsUpdateL
AccountSettings.KEY_CLIENT_SECRET,
clientSecretString
)
+
+ AuthStatePrefUtils.saveAuthState(context, account!!, authState.jsonSerializeString())
+
val result = Bundle()
- result.putString(AccountManager.KEY_ACCOUNT_NAME, account!!.name)
+ result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name)
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type)
result.putString(AccountManager.KEY_AUTHTOKEN, authState.accessToken)
response?.onResult(result)
+
+ authorizationService.dispose()
}
val result = Bundle()
diff --git a/app/src/main/kotlin/at/bitfire/davdroid/syncadapter/SyncManager.kt b/app/src/main/kotlin/at/bitfire/davdroid/syncadapter/SyncManager.kt
index 32b18322f439fdb1e11fab06424e89813f4d85d5..503ae7fc40ab48c509d5ba7a0e796359f02d886c 100644
--- a/app/src/main/kotlin/at/bitfire/davdroid/syncadapter/SyncManager.kt
+++ b/app/src/main/kotlin/at/bitfire/davdroid/syncadapter/SyncManager.kt
@@ -194,7 +194,8 @@ abstract class SyncManager, out CollectionType: L
val clientSecretString = accountSettings.credentials().clientSecret
val clientSecret = OpenIdUtils.getClientAuthentication(clientSecretString)
- AuthorizationService(context).performTokenRequest(tokenRequest, clientSecret) { tokenResponse, ex ->
+ val authorizationService = AuthorizationService(context)
+ authorizationService.performTokenRequest(tokenRequest, clientSecret) { tokenResponse, ex ->
authState.update(tokenResponse, ex)
accountSettings.credentials(
Credentials(
@@ -219,6 +220,8 @@ abstract class SyncManager, out CollectionType: L
executor.execute {
performSync(DEFAULT_RETRY_AFTER, DEFAULT_SECOND_RETRY_AFTER, DEFAULT_MAX_RETRY_TIME)
}
+
+ authorizationService.dispose()
}
}
diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt
index c6d17fb691d33be99edda9fcdde8febb81931ecc..327163806d60e1f24ec16b7344d58a55002ed906 100644
--- a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt
+++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt
@@ -13,6 +13,7 @@ import android.content.ComponentName
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
+import android.net.Uri
import android.os.Bundle
import android.provider.CalendarContract
import android.text.Editable
@@ -30,9 +31,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.viewModelScope
+import at.bitfire.davdroid.BuildConfig
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.InvalidAccountException
import at.bitfire.davdroid.R
+import at.bitfire.davdroid.authorization.IdentityProvider
import at.bitfire.davdroid.databinding.LoginAccountDetailsBinding
import at.bitfire.davdroid.db.AppDatabase
import at.bitfire.davdroid.db.Credentials
@@ -47,6 +50,7 @@ import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.syncadapter.AccountUtils
import at.bitfire.davdroid.syncadapter.SyncAllAccountWorker
import at.bitfire.davdroid.syncadapter.SyncWorker
+import at.bitfire.davdroid.util.AuthStatePrefUtils
import at.bitfire.vcard4android.GroupMethod
import com.google.android.material.snackbar.Snackbar
import com.nextcloud.android.utils.AccountManagerUtils
@@ -56,6 +60,8 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
+import net.openid.appauth.AuthorizationService
+import net.openid.appauth.EndSessionRequest
import java.util.logging.Level
import javax.inject.Inject
@@ -202,7 +208,6 @@ class AccountDetailsFragment : Fragment() {
return v.root
}
-
private fun notifyEdrive(name: String) {
val intent = Intent("foundation.e.drive.action.ADD_ACCOUNT")
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
@@ -300,6 +305,9 @@ class AccountDetailsFragment : Fragment() {
// create Android account
val userData = AccountSettings.initialUserData(credentials, baseURL, config.cookies, config.calDAV?.emails?.firstOrNull())
+
+ AuthStatePrefUtils.saveAuthState(context, account, credentials?.authState?.jsonSerializeString())
+
Logger.log.log(Level.INFO, "Creating Android account with initial config", arrayOf(account, userData))
val accountManager = AccountManager.get(context)
@@ -424,14 +432,18 @@ class AccountDetailsFragment : Fragment() {
if (userData.get(AccountSettings.KEY_EMAIL_ADDRESS) == accountManager
.getUserData(account, AccountSettings.KEY_EMAIL_ADDRESS)
) {
+ val authState = userData.getString(AccountSettings.KEY_AUTH_STATE)
+
accountManager.setUserData(
openIdAccount, AccountSettings.KEY_AUTH_STATE,
- userData.getString(AccountSettings.KEY_AUTH_STATE)
+ authState
)
accountManager.setUserData(
openIdAccount, AccountSettings.KEY_CLIENT_SECRET,
userData.getString(AccountSettings.KEY_CLIENT_SECRET)
)
+
+ AuthStatePrefUtils.saveAuthState(context, openIdAccount, authState)
}
}
}
diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/MurenaOpenIdAuthFragment.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/MurenaOpenIdAuthFragment.kt
index 8a5636accfa099f12f1f09e08f94ddfe05d34c00..ed1bea7a027eccd935de43e8662365a4248136a5 100644
--- a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/MurenaOpenIdAuthFragment.kt
+++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/MurenaOpenIdAuthFragment.kt
@@ -49,6 +49,6 @@ class MurenaOpenIdAuthFragment : OpenIdAuthenticationBaseFragment(IdentityProvid
return
}
- proceedNext(userName, "https://murena.io/remote.php/dav/files/$userName")
+ proceedNext(userName, "${IdentityProvider.MURENA.baseUrl}$userName")
}
}
diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/signout/OpenIdEndSessionActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/signout/OpenIdEndSessionActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..631e4b8f75200e1bb1bec926f08d20d32bc70e00
--- /dev/null
+++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/signout/OpenIdEndSessionActivity.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright MURENA SAS 2024
+ * 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 at.bitfire.davdroid.ui.signout
+
+import android.accounts.AccountManager
+import android.app.Activity
+import android.os.Bundle
+import at.bitfire.davdroid.authorization.IdentityProvider
+import at.bitfire.davdroid.settings.AccountSettings
+import net.openid.appauth.AuthState
+import net.openid.appauth.AuthorizationService
+import net.openid.appauth.AuthorizationServiceConfiguration
+import net.openid.appauth.EndSessionRequest
+
+class OpenIdEndSessionActivity : Activity() {
+
+ private var authorizationService: AuthorizationService? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val authStateString = intent.getStringExtra(AccountSettings.KEY_AUTH_STATE)
+ val accountType = intent.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE)
+
+ if (authStateString.isNullOrBlank() || accountType.isNullOrBlank()) {
+ finish()
+ return
+ }
+
+ val authState = AuthState.jsonDeserialize(authStateString)
+
+ val configuration = authState.authorizationServiceConfiguration
+ if (configuration?.endSessionEndpoint == null) {
+ finish()
+ return
+ }
+
+ startEndSessionWebIntent(
+ accountType = accountType,
+ configuration = configuration,
+ authState = authState
+ )
+
+ finish()
+ }
+
+ private fun startEndSessionWebIntent(
+ accountType: String,
+ configuration: AuthorizationServiceConfiguration,
+ authState: AuthState
+ ) {
+ authorizationService = AuthorizationService(applicationContext)
+
+ val redirectUri =
+ IdentityProvider.retrieveByAccountType(this, accountType)?.logoutRedirectUri
+
+ val intent = authorizationService!!.getEndSessionRequestIntent(
+ EndSessionRequest.Builder(configuration)
+ .setIdTokenHint(authState.idToken)
+ .setPostLogoutRedirectUri(redirectUri)
+ .build()
+ )
+
+ startActivity(intent)
+ }
+
+ override fun onDestroy() {
+ authorizationService?.dispose()
+ super.onDestroy()
+ }
+}
diff --git a/app/src/main/kotlin/at/bitfire/davdroid/util/AuthStatePrefUtils.kt b/app/src/main/kotlin/at/bitfire/davdroid/util/AuthStatePrefUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..19ad813d71f4875727f4ef3874e2826d581b326b
--- /dev/null
+++ b/app/src/main/kotlin/at/bitfire/davdroid/util/AuthStatePrefUtils.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright MURENA SAS 2024
+ * 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 at.bitfire.davdroid.util
+
+import android.accounts.Account
+import android.content.Context
+import android.content.SharedPreferences
+
+object AuthStatePrefUtils {
+
+ private const val AUTH_STATE_SHARED_PREF = "authStateShared_Pref"
+
+ fun saveAuthState(context: Context, account: Account, value: String?) {
+ val preferences = getSharedPref(context)
+ preferences.edit()
+ .putString(getKey(account), value)
+ .apply()
+ }
+
+ fun loadAuthState(context: Context, name: String, type: String): String? {
+ val preferences = getSharedPref(context)
+ val key = getKey(name = name, type = type)
+ val value = preferences.getString(key, "")
+ val authState = if (value.isNullOrBlank()) null else value
+
+ authState.let {
+ preferences.edit()
+ .remove(key)
+ .apply()
+ }
+
+ return authState
+ }
+
+ private fun getSharedPref(context: Context): SharedPreferences {
+ return context.getSharedPreferences(AUTH_STATE_SHARED_PREF, Context.MODE_PRIVATE)
+ }
+
+ private fun getKey(account: Account): String {
+ return getKey(name = account.name,
+ type = account.type)
+ }
+
+ private fun getKey(name: String, type: String): String {
+ return "$name==$type"
+ }
+}