2018-02-03 22:45:14 +01:00
|
|
|
/* Copyright 2018 Conny Duck
|
|
|
|
*
|
|
|
|
* This file is a part of Tusky.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* Tusky 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 Tusky; if not,
|
|
|
|
* see <http://www.gnu.org/licenses>. */
|
|
|
|
|
|
|
|
package com.keylesspalace.tusky.db
|
|
|
|
|
2022-10-18 19:38:17 +02:00
|
|
|
import android.content.Context
|
2018-02-03 22:45:14 +01:00
|
|
|
import android.util.Log
|
2022-10-18 19:38:17 +02:00
|
|
|
import androidx.preference.PreferenceManager
|
2018-02-03 22:45:14 +01:00
|
|
|
import com.keylesspalace.tusky.entity.Account
|
2018-11-12 21:09:39 +01:00
|
|
|
import com.keylesspalace.tusky.entity.Status
|
2022-10-18 19:38:17 +02:00
|
|
|
import com.keylesspalace.tusky.settings.PrefKeys
|
2021-06-28 21:13:24 +02:00
|
|
|
import java.util.Locale
|
2020-02-25 19:49:15 +01:00
|
|
|
import javax.inject.Inject
|
|
|
|
import javax.inject.Singleton
|
2018-02-03 22:45:14 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This class caches the account database and handles all account related operations
|
|
|
|
* @author ConnyDuck
|
|
|
|
*/
|
|
|
|
|
2018-02-12 22:03:08 +01:00
|
|
|
private const val TAG = "AccountManager"
|
|
|
|
|
2020-02-25 19:49:15 +01:00
|
|
|
@Singleton
|
|
|
|
class AccountManager @Inject constructor(db: AppDatabase) {
|
2018-02-03 22:45:14 +01:00
|
|
|
|
2018-03-27 19:47:00 +02:00
|
|
|
@Volatile
|
|
|
|
var activeAccount: AccountEntity? = null
|
2018-02-03 22:45:14 +01:00
|
|
|
|
2020-12-23 19:13:37 +01:00
|
|
|
var accounts: MutableList<AccountEntity> = mutableListOf()
|
|
|
|
private set
|
2018-03-27 19:47:00 +02:00
|
|
|
private val accountDao: AccountDao = db.accountDao()
|
2018-02-03 22:45:14 +01:00
|
|
|
|
|
|
|
init {
|
|
|
|
accounts = accountDao.loadAll().toMutableList()
|
|
|
|
|
|
|
|
activeAccount = accounts.find { acc ->
|
|
|
|
acc.isActive
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-06-30 20:49:48 +02:00
|
|
|
* Adds a new account and makes it the active account.
|
2018-02-03 22:45:14 +01:00
|
|
|
* @param accessToken the access token for the new account
|
|
|
|
* @param domain the domain of the accounts Mastodon instance
|
2022-06-30 20:49:48 +02:00
|
|
|
* @param clientId the oauth client id used to sign in the account
|
|
|
|
* @param clientSecret the oauth client secret used to sign in the account
|
|
|
|
* @param oauthScopes the oauth scopes granted to the account
|
|
|
|
* @param newAccount the [Account] as returned by the Mastodon Api
|
2018-02-03 22:45:14 +01:00
|
|
|
*/
|
2022-06-20 16:45:54 +02:00
|
|
|
fun addAccount(
|
|
|
|
accessToken: String,
|
|
|
|
domain: String,
|
|
|
|
clientId: String,
|
|
|
|
clientSecret: String,
|
2022-06-30 20:49:48 +02:00
|
|
|
oauthScopes: String,
|
|
|
|
newAccount: Account
|
2022-06-20 16:45:54 +02:00
|
|
|
) {
|
2018-02-03 22:45:14 +01:00
|
|
|
|
2018-03-27 19:47:00 +02:00
|
|
|
activeAccount?.let {
|
2018-02-03 22:45:14 +01:00
|
|
|
it.isActive = false
|
2018-03-27 19:47:00 +02:00
|
|
|
Log.d(TAG, "addAccount: saving account with id " + it.id)
|
2018-02-03 22:45:14 +01:00
|
|
|
|
|
|
|
accountDao.insertOrReplace(it)
|
|
|
|
}
|
2022-06-30 20:49:48 +02:00
|
|
|
// check if this is a relogin with an existing account, if yes update it, otherwise create a new one
|
2022-07-05 18:18:12 +02:00
|
|
|
val existingAccountIndex = accounts.indexOfFirst { account ->
|
2022-06-30 20:49:48 +02:00
|
|
|
domain == account.domain && newAccount.id == account.accountId
|
2022-07-05 18:18:12 +02:00
|
|
|
}
|
|
|
|
val newAccountEntity = if (existingAccountIndex != -1) {
|
|
|
|
accounts[existingAccountIndex].copy(
|
|
|
|
accessToken = accessToken,
|
|
|
|
clientId = clientId,
|
|
|
|
clientSecret = clientSecret,
|
|
|
|
oauthScopes = oauthScopes,
|
|
|
|
isActive = true
|
|
|
|
).also { accounts[existingAccountIndex] = it }
|
|
|
|
} else {
|
2022-06-30 20:49:48 +02:00
|
|
|
val maxAccountId = accounts.maxByOrNull { it.id }?.id ?: 0
|
|
|
|
val newAccountId = maxAccountId + 1
|
|
|
|
AccountEntity(
|
|
|
|
id = newAccountId,
|
|
|
|
domain = domain.lowercase(Locale.ROOT),
|
|
|
|
accessToken = accessToken,
|
|
|
|
clientId = clientId,
|
|
|
|
clientSecret = clientSecret,
|
|
|
|
oauthScopes = oauthScopes,
|
|
|
|
isActive = true,
|
|
|
|
accountId = newAccount.id
|
|
|
|
).also { accounts.add(it) }
|
|
|
|
}
|
|
|
|
|
|
|
|
activeAccount = newAccountEntity
|
|
|
|
updateActiveAccount(newAccount)
|
2018-02-03 22:45:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Saves an already known account to the database.
|
|
|
|
* New accounts must be created with [addAccount]
|
|
|
|
* @param account the account to save
|
|
|
|
*/
|
|
|
|
fun saveAccount(account: AccountEntity) {
|
2018-03-27 19:47:00 +02:00
|
|
|
if (account.id != 0L) {
|
|
|
|
Log.d(TAG, "saveAccount: saving account with id " + account.id)
|
2018-02-03 22:45:14 +01:00
|
|
|
accountDao.insertOrReplace(account)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Logs the current account out by deleting all data of the account.
|
|
|
|
* @return the new active account, or null if no other account was found
|
|
|
|
*/
|
2018-03-27 19:47:00 +02:00
|
|
|
fun logActiveAccountOut(): AccountEntity? {
|
2018-02-03 22:45:14 +01:00
|
|
|
|
2022-06-20 16:45:54 +02:00
|
|
|
return activeAccount?.let { account ->
|
|
|
|
|
|
|
|
account.logout()
|
|
|
|
|
|
|
|
accounts.remove(account)
|
|
|
|
accountDao.delete(account)
|
2018-02-03 22:45:14 +01:00
|
|
|
|
2018-03-27 19:47:00 +02:00
|
|
|
if (accounts.size > 0) {
|
2018-02-03 22:45:14 +01:00
|
|
|
accounts[0].isActive = true
|
|
|
|
activeAccount = accounts[0]
|
2018-03-27 19:47:00 +02:00
|
|
|
Log.d(TAG, "logActiveAccountOut: saving account with id " + accounts[0].id)
|
2018-02-03 22:45:14 +01:00
|
|
|
accountDao.insertOrReplace(accounts[0])
|
|
|
|
} else {
|
|
|
|
activeAccount = null
|
|
|
|
}
|
2022-06-20 16:45:54 +02:00
|
|
|
activeAccount
|
2018-02-03 22:45:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* updates the current account with new information from the mastodon api
|
|
|
|
* and saves it in the database
|
|
|
|
* @param account the [Account] object returned from the api
|
|
|
|
*/
|
|
|
|
fun updateActiveAccount(account: Account) {
|
2018-03-27 19:47:00 +02:00
|
|
|
activeAccount?.let {
|
2018-02-03 22:45:14 +01:00
|
|
|
it.accountId = account.id
|
|
|
|
it.username = account.username
|
2018-03-03 13:24:03 +01:00
|
|
|
it.displayName = account.name
|
2018-02-03 22:45:14 +01:00
|
|
|
it.profilePictureUrl = account.avatar
|
2018-11-12 21:09:39 +01:00
|
|
|
it.defaultPostPrivacy = account.source?.privacy ?: Status.Visibility.PUBLIC
|
2022-11-24 15:45:19 +01:00
|
|
|
it.defaultPostLanguage = account.source?.language ?: ""
|
2018-11-12 21:09:39 +01:00
|
|
|
it.defaultMediaSensitivity = account.source?.sensitive ?: false
|
2018-07-30 15:43:27 +02:00
|
|
|
it.emojis = account.emojis ?: emptyList()
|
2018-02-03 22:45:14 +01:00
|
|
|
|
2018-03-27 19:47:00 +02:00
|
|
|
Log.d(TAG, "updateActiveAccount: saving account with id " + it.id)
|
2022-06-30 20:49:48 +02:00
|
|
|
accountDao.insertOrReplace(it)
|
2018-02-03 22:45:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* changes the active account
|
|
|
|
* @param accountId the database id of the new active account
|
|
|
|
*/
|
|
|
|
fun setActiveAccount(accountId: Long) {
|
|
|
|
|
2018-03-27 19:47:00 +02:00
|
|
|
activeAccount?.let {
|
|
|
|
Log.d(TAG, "setActiveAccount: saving account with id " + it.id)
|
2018-02-03 22:45:14 +01:00
|
|
|
it.isActive = false
|
2018-03-02 21:25:58 +01:00
|
|
|
saveAccount(it)
|
2018-02-03 22:45:14 +01:00
|
|
|
}
|
|
|
|
|
2020-02-25 19:49:15 +01:00
|
|
|
activeAccount = accounts.find { (id) ->
|
|
|
|
id == accountId
|
2018-02-03 22:45:14 +01:00
|
|
|
}
|
|
|
|
|
2018-03-27 19:47:00 +02:00
|
|
|
activeAccount?.let {
|
2018-02-03 22:45:14 +01:00
|
|
|
it.isActive = true
|
|
|
|
accountDao.insertOrReplace(it)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return an immutable list of all accounts in the database with the active account first
|
|
|
|
*/
|
|
|
|
fun getAllAccountsOrderedByActive(): List<AccountEntity> {
|
2019-02-16 14:49:17 +01:00
|
|
|
val accountsCopy = accounts.toMutableList()
|
2020-10-25 18:36:00 +01:00
|
|
|
accountsCopy.sortWith { l, r ->
|
2018-02-03 22:45:14 +01:00
|
|
|
when {
|
|
|
|
l.isActive && !r.isActive -> -1
|
|
|
|
r.isActive && !l.isActive -> 1
|
|
|
|
else -> 0
|
|
|
|
}
|
2020-10-25 18:36:00 +01:00
|
|
|
}
|
2018-02-03 22:45:14 +01:00
|
|
|
|
2019-02-16 14:49:17 +01:00
|
|
|
return accountsCopy
|
2018-02-03 22:45:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true if at least one account has notifications enabled
|
|
|
|
*/
|
2018-02-12 22:03:08 +01:00
|
|
|
fun areNotificationsEnabled(): Boolean {
|
2018-02-03 22:45:14 +01:00
|
|
|
return accounts.any { it.notificationsEnabled }
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finds an account by its database id
|
|
|
|
* @param accountId the id of the account
|
|
|
|
* @return the requested account or null if it was not found
|
|
|
|
*/
|
|
|
|
fun getAccountById(accountId: Long): AccountEntity? {
|
2020-02-25 19:49:15 +01:00
|
|
|
return accounts.find { (id) ->
|
|
|
|
id == accountId
|
2018-02-03 22:45:14 +01:00
|
|
|
}
|
|
|
|
}
|
2022-05-17 19:32:09 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Finds an account by its string identifier
|
|
|
|
* @param identifier the string identifier of the account
|
|
|
|
* @return the requested account or null if it was not found
|
|
|
|
*/
|
|
|
|
fun getAccountByIdentifier(identifier: String): AccountEntity? {
|
|
|
|
return accounts.find {
|
|
|
|
identifier == it.identifier
|
|
|
|
}
|
|
|
|
}
|
2022-10-18 19:38:17 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true if the name of the currently-selected account should be displayed in UIs
|
|
|
|
*/
|
|
|
|
fun shouldDisplaySelfUsername(context: Context): Boolean {
|
|
|
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
|
|
|
val showUsernamePreference = sharedPreferences.getString(PrefKeys.SHOW_SELF_USERNAME, "disambiguate")
|
|
|
|
if (showUsernamePreference == "always")
|
|
|
|
return true
|
|
|
|
if (showUsernamePreference == "never")
|
|
|
|
return false
|
|
|
|
|
|
|
|
return accounts.size > 1 // "disambiguate"
|
|
|
|
}
|
2021-06-28 21:13:24 +02:00
|
|
|
}
|