Multi account feature (#490)
* basic implementation * improve LoginActivity * darken drawer background image * add current avatar in ComposeActivity * add account name to logout dialog * multi account support for notifications * multi account support for notifications * bugfixes & cleanup * fix bug where somethings notifications would open with the wrong user * correctly set active account in SFragment * small improvements
This commit is contained in:
parent
c9004f1d54
commit
92ae463b38
40 changed files with 1293 additions and 773 deletions
31
app/src/main/java/com/keylesspalace/tusky/db/AccountDao.kt
Normal file
31
app/src/main/java/com/keylesspalace/tusky/db/AccountDao.kt
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* 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
|
||||
|
||||
import android.arch.persistence.room.*
|
||||
|
||||
@Dao
|
||||
interface AccountDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertOrReplace(account: AccountEntity): Long
|
||||
|
||||
@Delete
|
||||
fun delete(account: AccountEntity)
|
||||
|
||||
@Query("SELECT * FROM AccountEntity ORDER BY id ASC")
|
||||
fun loadAll(): List<AccountEntity>
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/* 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
|
||||
|
||||
import android.arch.persistence.room.Entity
|
||||
import android.arch.persistence.room.Index
|
||||
import android.arch.persistence.room.PrimaryKey
|
||||
|
||||
@Entity(indices = [Index(value = ["domain", "accountId"],
|
||||
unique = true)])
|
||||
data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
|
||||
val domain: String,
|
||||
var accessToken: String,
|
||||
var isActive: Boolean,
|
||||
var accountId: String = "",
|
||||
var username: String = "",
|
||||
var displayName: String = "",
|
||||
var profilePictureUrl: String = "",
|
||||
var notificationsEnabled: Boolean = true,
|
||||
var notificationsMentioned: Boolean = true,
|
||||
var notificationsFollowed: Boolean = true,
|
||||
var notificationsReblogged: Boolean = true,
|
||||
var notificationsFavorited: Boolean = true,
|
||||
var notificationSound: Boolean = true,
|
||||
var notificationVibration: Boolean = true,
|
||||
var notificationLight: Boolean = true,
|
||||
var lastNotificationId: String = "0",
|
||||
var activeNotifications: String = "[]") {
|
||||
|
||||
val identifier: String
|
||||
get() = "$domain:$accountId"
|
||||
|
||||
val fullName: String
|
||||
get() = "@$username@$domain"
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as AccountEntity
|
||||
|
||||
if (id == other.id) return true
|
||||
if (domain == other.domain && accountId == other.accountId) return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id.hashCode()
|
||||
result = 31 * result + domain.hashCode()
|
||||
result = 31 * result + accountId.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
190
app/src/main/java/com/keylesspalace/tusky/db/AccountManager.kt
Normal file
190
app/src/main/java/com/keylesspalace/tusky/db/AccountManager.kt
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/* 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
|
||||
|
||||
import android.util.Log
|
||||
import com.keylesspalace.tusky.TuskyApplication
|
||||
import com.keylesspalace.tusky.entity.Account
|
||||
|
||||
/**
|
||||
* This class caches the account database and handles all account related operations
|
||||
* @author ConnyDuck
|
||||
*/
|
||||
|
||||
class AccountManager {
|
||||
|
||||
@Volatile var activeAccount: AccountEntity? = null
|
||||
|
||||
private var accounts: MutableList<AccountEntity> = mutableListOf()
|
||||
private val accountDao: AccountDao = TuskyApplication.getDB().accountDao()
|
||||
|
||||
init {
|
||||
accounts = accountDao.loadAll().toMutableList()
|
||||
|
||||
activeAccount = accounts.find { acc ->
|
||||
acc.isActive
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new empty account and makes it the active account.
|
||||
* More account information has to be added later with [updateActiveAccount]
|
||||
* or the account wont be saved to the database.
|
||||
* @param accessToken the access token for the new account
|
||||
* @param domain the domain of the accounts Mastodon instance
|
||||
*/
|
||||
fun addAccount(accessToken: String, domain: String) {
|
||||
|
||||
activeAccount?.let{
|
||||
it.isActive = false
|
||||
Log.d("AccountManager", "saving account with id "+it.id)
|
||||
|
||||
accountDao.insertOrReplace(it)
|
||||
}
|
||||
|
||||
activeAccount = AccountEntity(id = 0, domain = domain, accessToken = accessToken, isActive = true)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if(account.id != 0L) {
|
||||
Log.d("AccountManager", "saving account with id "+account.id)
|
||||
val index = accounts.indexOf(account)
|
||||
if (index != -1) {
|
||||
accounts.removeAt(index)
|
||||
accounts.add(account)
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
fun logActiveAccountOut() : AccountEntity? {
|
||||
|
||||
if(activeAccount == null) {
|
||||
return null
|
||||
} else {
|
||||
accounts.remove(activeAccount!!)
|
||||
accountDao.delete(activeAccount!!)
|
||||
|
||||
if(accounts.size > 0) {
|
||||
accounts[0].isActive = true
|
||||
activeAccount = accounts[0]
|
||||
accountDao.insertOrReplace(accounts[0])
|
||||
} else {
|
||||
activeAccount = null
|
||||
}
|
||||
return activeAccount
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
activeAccount?.let{
|
||||
it.accountId = account.id
|
||||
it.username = account.username
|
||||
it.displayName = account.getDisplayName()
|
||||
it.profilePictureUrl = account.avatar
|
||||
|
||||
Log.d("AccountManager", "id before save "+it.id)
|
||||
it.id = accountDao.insertOrReplace(it)
|
||||
Log.d("AccountManager", "id after save "+it.id)
|
||||
|
||||
|
||||
val accountIndex = accounts.indexOf(it)
|
||||
|
||||
if(accountIndex != -1) {
|
||||
//in case the user was already logged in with this account, remove the old information
|
||||
accounts.removeAt(accountIndex)
|
||||
accounts.add(accountIndex, it)
|
||||
} else {
|
||||
accounts.add(it)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* changes the active account
|
||||
* @param accountId the database id of the new active account
|
||||
*/
|
||||
fun setActiveAccount(accountId: Long) {
|
||||
|
||||
activeAccount?.let{
|
||||
it.isActive = false
|
||||
accountDao.insertOrReplace(it)
|
||||
}
|
||||
|
||||
activeAccount = accounts.find { acc ->
|
||||
acc.id == accountId
|
||||
}
|
||||
|
||||
activeAccount?.let{
|
||||
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> {
|
||||
accounts.sortWith (Comparator { l, r ->
|
||||
when {
|
||||
l.isActive && !r.isActive -> -1
|
||||
r.isActive && !l.isActive -> 1
|
||||
else -> 0
|
||||
}
|
||||
})
|
||||
|
||||
return accounts.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if at least one account has notifications enabled
|
||||
*/
|
||||
fun notificationsEnabled(): Boolean {
|
||||
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? {
|
||||
return accounts.find { acc ->
|
||||
acc.id == accountId
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -25,10 +25,11 @@ import android.support.annotation.NonNull;
|
|||
* DB version & declare DAO
|
||||
*/
|
||||
|
||||
@Database(entities = {TootEntity.class}, version = 4, exportSchema = false)
|
||||
@Database(entities = {TootEntity.class, AccountEntity.class}, version = 4, exportSchema = false)
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
|
||||
public abstract TootDao tootDao();
|
||||
public abstract AccountDao accountDao();
|
||||
|
||||
public static final Migration MIGRATION_2_3 = new Migration(2, 3) {
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue