Refactor AccountManagerActivity.kt to use User Id instead of username

MAILAND-1100 #comment Affected: AccountManagerActivity.kt
This commit is contained in:
Davide Farella 2020-11-12 10:16:19 +01:00
parent c8a199ead0
commit 0af7f1ef01
5 changed files with 148 additions and 109 deletions

View File

@ -96,6 +96,8 @@ public abstract class BaseActivity extends AppCompatActivity implements INetwork
@Inject
protected NetworkConfigurator networkConfigurator;
@Inject
@Deprecated // TODO this should not be used by sub-classes, they should get it injected
// directly, as are aiming to remove this base class
protected UserManager mUserManager;
@Inject
protected JobManager mJobManager;

View File

@ -20,34 +20,37 @@ package ch.protonmail.android.activities.multiuser
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.Menu
import android.view.MenuItem
import androidx.activity.viewModels
import androidx.lifecycle.Observer
import androidx.core.view.postDelayed
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import ch.protonmail.android.R
import ch.protonmail.android.activities.BaseActivity
import ch.protonmail.android.activities.multiuser.viewModel.AccountManagerViewModel
import ch.protonmail.android.adapters.AccountsAdapter
import ch.protonmail.android.api.AccountManager
import ch.protonmail.android.core.Constants
import ch.protonmail.android.core.ProtonMailApplication
import ch.protonmail.android.core.UserManager
import ch.protonmail.android.domain.entity.user.User
import ch.protonmail.android.events.LogoutEvent
import ch.protonmail.android.uiModel.DrawerUserModel
import ch.protonmail.android.utils.extensions.app
import ch.protonmail.android.utils.extensions.setBarColors
import ch.protonmail.android.utils.extensions.showToast
import ch.protonmail.android.utils.moveToMailbox
import ch.protonmail.android.utils.moveToMailboxLogout
import ch.protonmail.android.utils.ui.dialogs.DialogUtils
import ch.protonmail.android.utils.ui.dialogs.DialogUtils.Companion.showTwoButtonInfoDialog
import com.squareup.otto.Subscribe
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.activity_account_manager.*
import kotlinx.android.synthetic.main.toolbar_white.*
import javax.inject.Inject
@AndroidEntryPoint
class AccountManagerActivity : BaseActivity() {
private var movingToMailbox = false
private val viewModel by viewModels<AccountManagerViewModel>()
@ -57,45 +60,59 @@ class AccountManagerActivity : BaseActivity() {
* to display the users (logged in and recently logged out) of the application.
*/
private val accountsAdapter by lazy { AccountsAdapter() }
override fun getLayoutId() = R.layout.activity_account_manager
@Inject
lateinit var accountManager: AccountManager
@Inject
lateinit var userManager: UserManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
toolbar.setNavigationIcon(R.drawable.ic_close)
setSupportActionBar(toolbar)
val actionBar = supportActionBar
actionBar?.apply {
setDisplayHomeAsUpEnabled(true)
}
window?.apply {
setBarColors(resources.getColor(R.color.new_purple_dark))
}
actionBar?.setDisplayHomeAsUpEnabled(true)
window?.setBarColors(getColor(R.color.new_purple_dark))
val nextLoggedInAccount = mUserManager.nextLoggedInAccountOtherThanCurrent
val currentActiveAccount = mUserManager.username
accountsAdapter.apply {
onLogoutAccount = { username ->
DialogUtils.showInfoDialogWithTwoButtons(this@AccountManagerActivity,
if (nextLoggedInAccount != null && username == currentActiveAccount) getString(R.string.logout) else String.format(getString(R.string.log_out), username),
if (nextLoggedInAccount != null && username == currentActiveAccount) String.format(getString(R.string.logout_question_next_account), nextLoggedInAccount) else getString(R.string.logout_question),
getString(R.string.cancel),
getString(R.string.okay), {
viewModel.logoutAccountResult.observe(this@AccountManagerActivity, Observer(::closeActivity))
viewModel.logoutAccount(username)
}, false)
}
onRemoveAccount = { username ->
DialogUtils.showInfoDialogWithTwoButtons(this@AccountManagerActivity,
getString(R.string.logout), String.format(getString(R.string.remove_account_question), username), getString(R.string.cancel),
getString(R.string.okay), {
viewModel.removedAccountResult.observe(this@AccountManagerActivity, Observer(::closeActivity))
viewModel.removeAccount(username)
}, false)
}
}
onLogoutAccount = { userId ->
lifecycleScope.launchWhenCreated {
val nextLoggedInUserId = userManager.getNextLoggedInUser()
val (title, message) = if (nextLoggedInUserId != null) {
val next = userManager.getUser(nextLoggedInUserId)
getString(R.string.logout) to getString(R.string.logout_question_next_account, next.name.s)
} else {
val current = checkNotNull(userManager.getCurrentUser())
getString(R.string.log_out, current.name.s) to getString(R.string.logout_question)
}
showTwoButtonInfoDialog(this@AccountManagerActivity, title, message) {
viewModel.logoutAccountResult.observe(this@AccountManagerActivity, ::closeActivity)
viewModel.logout(userId)
}
}
}
onRemoveAccount = { userId ->
lifecycleScope.launchWhenCreated {
val username = checkNotNull(userManager.getUser(userId)).name.s
showTwoButtonInfoDialog(
context = this@AccountManagerActivity,
title = getString(R.string.logout),
message = getString(R.string.remove_account_question, username)
) {
viewModel.removedAccountResult.observe(this@AccountManagerActivity, ::closeActivity)
viewModel.remove(userId)
}
}
}
}
accountsRecyclerView.layoutManager = LinearLayoutManager(this)
}
@ -103,9 +120,7 @@ class AccountManagerActivity : BaseActivity() {
private fun closeAndMoveToLogin(result: Boolean) {
if (result && !movingToMailbox) {
movingToMailbox = true
Handler(Looper.getMainLooper()).postDelayed({
moveToMailboxLogout()
}, 500)
window.decorView.postDelayed(500, ::moveToMailboxLogout)
}
}
@ -126,7 +141,7 @@ class AccountManagerActivity : BaseActivity() {
override fun onStart() {
super.onStart()
ProtonMailApplication.getApplication().bus.register(this)
app.bus.register(this)
loadData()
}
@ -137,7 +152,7 @@ class AccountManagerActivity : BaseActivity() {
override fun onStop() {
super.onStop()
ProtonMailApplication.getApplication().bus.unregister(this)
app.bus.unregister(this)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -154,9 +169,8 @@ class AccountManagerActivity : BaseActivity() {
}
R.id.action_remove_all -> {
showToast(R.string.account_manager_remove_all_accounts)
val accountManager = AccountManager.getInstance(this)
viewModel.removedAllAccountsResult.observe(this@AccountManagerActivity, Observer(::closeAndMoveToLogin))
viewModel.removeAllAccounts(accountManager.getLoggedInUsers())
viewModel.removedAllAccountsResult.observe(this@AccountManagerActivity, ::closeAndMoveToLogin)
viewModel.removeAllLoggedIn()
true
}
else -> {
@ -166,31 +180,45 @@ class AccountManagerActivity : BaseActivity() {
}
private fun loadData() {
val currentPrimaryAccount = mUserManager.username
if (!mUserManager.accessTokenExists() || mUserManager.getUser(currentPrimaryAccount).addresses.isEmpty()) {
mUserManager.logoutOffline(currentPrimaryAccount)
lifecycleScope.launchWhenCreated {
val hasAccessToken = userManager.accessTokenExists()
val hasAddresses = userManager.getCurrentUser()?.addresses?.hasAddresses ?: false
if (hasAccessToken.not() || hasAddresses.not()) {
userManager.logoutOffline(checkNotNull(userManager.currentUserId))
}
val currentUser = userManager.currentUserId
lifecycleScope.launchWhenCreated {
val allUsers = accountManager.allLoggedIn().map { it to true } +
accountManager.allLoggedOut().map { it to false }
val accounts = allUsers
// Current user as first position, then logged in first
.sortedByDescending { it.first == currentUser && it.second }
.map { (id, loggedIn) ->
val user = userManager.getUser(id)
user.toUiModel(loggedIn, id == currentUser)
}
accountsAdapter.items = accounts + DrawerUserModel.Footer
accountsRecyclerView.adapter = accountsAdapter
}
}
val accountManager = AccountManager.getInstance(this)
val accounts = accountManager.getLoggedInUsers().map {
val userAddresses = mUserManager.getUser(it).addresses
val primaryAddress = userAddresses.find { address ->
address.type == Constants.ADDRESS_TYPE_PRIMARY
}
val displayName = primaryAddress?.displayName ?: ""
val primaryAddressEmail = if (primaryAddress == null) {
it
} else {
primaryAddress.email
}
DrawerUserModel.BaseUser.AccountUser(it, primaryAddressEmail, true, it == currentPrimaryAccount, if (displayName.isNotEmpty()) displayName else it)
}.sortedByDescending {
it.primary
}.plus(accountManager.getSavedUsers().map {
DrawerUserModel.BaseUser.AccountUser(name = it, loggedIn = false, emailAddress = "", primary = false, displayName = it)
}) as MutableList<DrawerUserModel>
accounts.add(DrawerUserModel.AccFooter)
accountsAdapter.items = accounts
accountsRecyclerView.adapter = accountsAdapter
}
private fun User.toUiModel(loggedIn: Boolean, currentPrimary: Boolean): DrawerUserModel.BaseUser.AccountUser {
val primaryAddress = addresses.primary
val username = name.s
val displayName = primaryAddress?.displayName?.s
return DrawerUserModel.BaseUser.AccountUser(
id = id,
name = displayName ?: username,
emailAddress = primaryAddress?.email?.s ?: username,
loggedIn = loggedIn,
primary = currentPrimary,
displayName = displayName ?: username
)
}
override fun onBackPressed() {

View File

@ -27,7 +27,6 @@ import ch.protonmail.android.api.AccountManager
import ch.protonmail.android.core.UserManager
import ch.protonmail.android.domain.entity.Id
import kotlinx.coroutines.launch
import me.proton.core.util.kotlin.unsupported
class AccountManagerViewModel @ViewModelInject constructor(
private val userManager: UserManager,
@ -52,11 +51,6 @@ class AccountManagerViewModel @ViewModelInject constructor(
}
}
@Deprecated("Use with user id", ReplaceWith("logout(userId)"), DeprecationLevel.ERROR)
fun logoutAccount(username: String) {
unsupported
}
fun remove(userId: Id, notify: Boolean = true) {
viewModelScope.launch {
@ -69,14 +63,9 @@ class AccountManagerViewModel @ViewModelInject constructor(
}
}
@Deprecated("Use with user id", ReplaceWith("remove(userId, notify)"), DeprecationLevel.ERROR)
fun removeAccount(username: String, notify: Boolean = true) {
unsupported
}
fun removeAll(usersIds: Collection<Id>) {
fun removeAllLoggedIn() {
viewModelScope.launch {
val otherUsersIds = (usersIds - userManager.currentUserId).filterNotNull()
val otherUsersIds = (accountManager.allLoggedIn() - userManager.currentUserId).filterNotNull()
for (userId in otherUsersIds) {
remove(userId, notify = false)
}
@ -86,9 +75,4 @@ class AccountManagerViewModel @ViewModelInject constructor(
}
}
}
@Deprecated("Use with user id", ReplaceWith("removeAll(usersIds)"), DeprecationLevel.ERROR)
fun removeAllAccounts(listUsername: List<String>) {
unsupported
}
}

View File

@ -53,30 +53,16 @@ internal sealed class DrawerUserModel {
*/
open val notificationsSnoozed: Boolean = false
data class AccountUser @JvmOverloads constructor(
data class AccountUser(
override val id: Id,
override val name: String = EMPTY_STRING,
override val emailAddress: String = EMPTY_STRING,
override val loggedIn: Boolean = false,
val primary: Boolean = false,
val displayName: String = EMPTY_STRING
) : BaseUser() {
override val name: String,
override val emailAddress: String,
override val loggedIn: Boolean,
val primary: Boolean,
val displayName: String
) : BaseUser()
@Deprecated(
"Use constructor with id",
ReplaceWith("AccountUser(id, name, emailAddress, loggedIn, primary, displayName"),
DeprecationLevel.ERROR
)
constructor(
name: String = EMPTY_STRING,
emailAddress: String = EMPTY_STRING,
loggedIn: Boolean = false,
primary: Boolean = false,
displayName: String = EMPTY_STRING
): this(TODO(), name, emailAddress, loggedIn, primary, displayName)
}
data class DrawerUser @JvmOverloads constructor(
data class DrawerUser(
override val id: Id,
override val name: String,
override val emailAddress: String,

View File

@ -1,18 +1,18 @@
/*
* Copyright (c) 2020 Proton Technologies AG
*
*
* This file is part of ProtonMail.
*
*
* ProtonMail 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.
*
*
* ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
*/
@ -32,6 +32,7 @@ import android.widget.CheckBox
import android.widget.EditText
import android.widget.TextView
import android.widget.ToggleButton
import androidx.annotation.StringRes
import ch.protonmail.android.R
import ch.protonmail.android.views.CustomFontButton
import com.google.android.material.snackbar.Snackbar
@ -107,6 +108,44 @@ class DialogUtils {
return dialog
}
inline fun showTwoButtonInfoDialog(
context: Context,
title: CharSequence,
message: CharSequence,
@StringRes positiveStringId: Int = R.string.okay,
@StringRes negativeStringId: Int = R.string.cancel,
cancelable: Boolean = false,
crossinline onPositive: () -> Unit
) {
AlertDialog.Builder(context)
.setTitle(title)
.setMessage(message)
.setNegativeButton(negativeStringId) { dialog, _ -> dialog.dismiss() }
.setPositiveButton(positiveStringId) { dialog, _ ->
run {
onPositive()
dialog.dismiss()
}
}
.setCancelable(cancelable)
.create()
.show()
}
@Deprecated(
"Use 'showTwoButtonInfoDialog'",
ReplaceWith(
"showTwoButtonInfoDialog(\n" +
" context,\n" +
" title,\n" +
" message,\n" +
" positiveStringId,\n" +
" negativeStringId,\n" +
" cancelable,\n" +
" okListener\n" +
")"
)
)
fun showInfoDialogWithTwoButtons(context: Context, title: String, message: String,
negativeBtnText: String, positiveBtnText: String,
okListener: ((Unit) -> Unit)?, cancelable: Boolean) {