Replaced Account Switcher by Core AccountPrimaryView.

Upgraded Dagger/Hilt to version 2.35.1.
This commit is contained in:
Neil Marietta 2021-05-13 00:52:59 +02:00
parent 0358af085d
commit 2a9532fe93
23 changed files with 182 additions and 982 deletions

View File

@ -263,6 +263,8 @@ dependencies {
`Proton-account-manager`,
`Proton-user`,
`Proton-key`,
`Proton-human-verification`,
`Proton-country`,
// Modules
project(Module.domain),
@ -303,9 +305,6 @@ dependencies {
// Hilt
`hilt-android`,
`hilt-androidx-annotations`,
// `hilt-androidx-viewModel`,
`hilt-androidx-view-model`, // special case as this currently does not follow other hilt-androidx version
`hilt-androidx-workManager`,
// Retrofit

View File

@ -30,16 +30,12 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Observer
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import ch.protonmail.android.BuildConfig
import ch.protonmail.android.R
import ch.protonmail.android.activities.dialogs.QuickSnoozeDialogFragment
import ch.protonmail.android.activities.multiuser.AccountManagerActivity
import ch.protonmail.android.activities.navigation.LabelWithUnreadCounter
import ch.protonmail.android.activities.navigation.NavigationViewModel
import ch.protonmail.android.activities.settings.EXTRA_CURRENT_MAILBOX_LABEL_ID
import ch.protonmail.android.activities.settings.EXTRA_CURRENT_MAILBOX_LOCATION
import ch.protonmail.android.adapters.AccountsAdapter
import ch.protonmail.android.adapters.mapLabelsToDrawerLabels
import ch.protonmail.android.api.AccountManager
import ch.protonmail.android.api.local.SnoozeSettings
@ -50,10 +46,7 @@ import ch.protonmail.android.contacts.ContactsActivity
import ch.protonmail.android.core.Constants
import ch.protonmail.android.core.UserManager
import ch.protonmail.android.data.local.MessageDatabase
import ch.protonmail.android.domain.entity.EmailAddress
import ch.protonmail.android.domain.entity.Id
import ch.protonmail.android.domain.entity.Name
import ch.protonmail.android.domain.entity.user.User
import ch.protonmail.android.feature.account.AccountStateManager
import ch.protonmail.android.mapper.LabelUiModelMapper
import ch.protonmail.android.prefs.SecureSharedPreferences
@ -62,7 +55,6 @@ import ch.protonmail.android.settings.pin.ValidatePinActivity
import ch.protonmail.android.ui.view.ProtonSideDrawer
import ch.protonmail.android.uiModel.DrawerItemUiModel.*
import ch.protonmail.android.uiModel.DrawerItemUiModel.Primary.Static.*
import ch.protonmail.android.uiModel.DrawerUserModel
import ch.protonmail.android.uiModel.LabelUiModel
import ch.protonmail.android.utils.AppUtil
import ch.protonmail.android.utils.UiUtil
@ -75,17 +67,13 @@ import ch.protonmail.android.utils.startSplashActivity
import ch.protonmail.android.utils.ui.dialogs.DialogUtils
import ch.protonmail.android.utils.ui.dialogs.DialogUtils.Companion.showTwoButtonInfoDialog
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import me.proton.core.account.domain.entity.Account
import me.proton.core.account.domain.entity.isReady
import me.proton.core.domain.entity.UserId
import java.util.Calendar
import javax.inject.Inject
import ch.protonmail.android.api.models.User as LegacyUser
// region constants
const val EXTRA_FIRST_LOGIN = "extra.first.login"
@ -99,24 +87,15 @@ const val REQUEST_CODE_SNOOZED_NOTIFICATIONS = 555
* needs support for the navigation drawer.
*/
@AndroidEntryPoint
abstract class NavigationActivity :
BaseActivity(),
QuickSnoozeDialogFragment.QuickSnoozeListener {
abstract class NavigationActivity : BaseActivity() {
// region views
private val toolbar by lazy { findViewById<Toolbar>(R.id.toolbar) }
private val drawerLayout: DrawerLayout by lazy { findViewById(R.id.drawer_layout) }
private val sideDrawer: ProtonSideDrawer by lazy { findViewById(R.id.sideDrawer) }
private val navigationDrawerUsersRecyclerView by lazy { findViewById<RecyclerView>(R.id.left_drawer_users) }
private lateinit var drawerToggle: ActionBarDrawerToggle
// endregion
/**
* [AccountsAdapter] for the Drawer. It is used as a replacement to the default [navigationDrawerRecyclerView]
* to display the users (logged in and recently logged out) of the application.
*/
private val accountsAdapter = AccountsAdapter()
val lazyManager = resettableManager()
val messagesDatabase by resettableLazy(lazyManager) {
@ -190,10 +169,6 @@ abstract class NavigationActivity :
.launchIn(lifecycleScope)
}
sideDrawer.setOnHeaderUserClick {
onUserClicked(true)
}
sideDrawer.setOnItemClick { drawerItem ->
// Header clicked
if (drawerItem is Primary) {
@ -208,12 +183,6 @@ abstract class NavigationActivity :
drawerLayout.closeDrawer(GravityCompat.START)
}
}
accountsAdapter.onItemClick = { account ->
if (account is DrawerUserModel.BaseUser) {
accountStateManager.switch(account.id)
}
}
}
override fun onNewIntent(intent: Intent) {
@ -242,14 +211,6 @@ abstract class NavigationActivity :
app.bus.unregister(this)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == RESULT_OK && requestCode == REQUEST_CODE_SNOOZED_NOTIFICATIONS) {
refreshDrawerHeader(checkNotNull(userManager.currentUser))
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
private fun checkUserId() {
// Requested UserId match the current ?
intent.extras?.getString(EXTRA_USER_ID)?.let { extraUserId ->
@ -265,7 +226,6 @@ abstract class NavigationActivity :
protected fun closeDrawer(ignoreIfPossible: Boolean = false): Boolean {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
var closeIt = true
onUserClicked(false)
if (!ignoreIfPossible) {
closeIt = false
}
@ -296,9 +256,6 @@ abstract class NavigationActivity :
drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START)
setUpInitialDrawerItems(userManager.currentLegacyUser?.isUsePin ?: false)
// LayoutManager set from xml
navigationDrawerUsersRecyclerView.adapter = accountsAdapter
drawerLayout.addDrawerListener(object : DrawerLayout.SimpleDrawerListener() {
/**
@ -315,7 +272,6 @@ abstract class NavigationActivity :
*/
override fun onDrawerClosed(drawerView: View) {
super.onDrawerClosed(drawerView)
onUserClicked(false)
onDrawerClose()
onDrawerClose = {}
}
@ -323,43 +279,8 @@ abstract class NavigationActivity :
navigationViewModel.labelsWithUnreadCounterLiveData().observe(this, CreateLabelsMenuObserver())
navigationViewModel.locationsUnreadLiveData().observe(this, LocationsMenuObserver())
lifecycleScope.launchWhenCreated {
userManager.currentUser?.let { refreshDrawerHeader(it) }
}
setupAccountsList()
}
protected fun setupAccountsList() {
navigationViewModel.reloadDependencies()
navigationViewModel.notificationsCounts()
navigationViewModel.notificationsCounterLiveData.observe(this) { counters ->
lifecycleScope.launchWhenCreated {
val accounts = accountStateManager.getSortedAccounts().first().map { account ->
val id = Id(account.userId.id)
val user = userManager.getLegacyUserOrNull(id)
account.toDrawerUser(account.isReady(), counters[id] ?: 0, user)
}
accountsAdapter.items = accounts + DrawerUserModel.Footer
}
}
}
private fun Account.toDrawerUser(
loggedIn: Boolean,
notificationsCount: Int,
user: LegacyUser?
) = DrawerUserModel.BaseUser.DrawerUser(
id = userId,
name = username,
emailAddress = email ?: user?.defaultAddressEmail ?: username,
loggedIn = loggedIn,
notifications = notificationsCount,
notificationsSnoozed = areNotificationSnoozedBlocking(Id(userId.id)),
displayName = user?.displayName ?: username
)
private suspend fun areNotificationsSnoozed(userId: Id): Boolean {
val userPreferences = SecureSharedPreferences.getPrefsForUser(this, userId)
with(SnoozeSettings.load(userPreferences)) {
@ -407,49 +328,6 @@ abstract class NavigationActivity :
)
}
protected fun refreshDrawerHeader(currentUser: User) {
val addresses = currentUser.addresses
if (addresses.hasAddresses) {
val address = checkNotNull(addresses.primary)
val displayName = address.displayName ?: Name(address.email.s)
sideDrawer.setUser(getInitials(displayName, address.email), displayName, address.email)
}
}
private fun getInitials(displayName: Name, emailAddress: EmailAddress): Pair<Char, Char> =
// TODO: definine initials of we can't extract them properly
getInitials(displayName.s) ?: getInitials(emailAddress.s) ?: '?' to '?'
private fun getInitials(displayNameOrEmailAddress: String): Pair<Char, Char>? {
val string = displayNameOrEmailAddress.toUpperCase()
return string
// split words
.split(" ")
// take first char of each word
.map { it.first() }
// take only letters or digits
.filter { it.isLetterOrDigit() }
// Valid only if we have 2 or more chars
.takeIf { it.size >= 2 }?.let { it[0] to it[1] }
// Otherwise take only first 2 letters or digits of the name
?: string
.filter { it.isLetterOrDigit() }
.takeIf { it.length >= 2 }
?.let { it[0] to it[1] }
}
fun onUserClicked(open: Boolean) {
sideDrawer.visibility = if (open) View.GONE else View.VISIBLE
navigationDrawerUsersRecyclerView.visibility = if (open) View.VISIBLE else View.GONE
}
override fun onQuickSnoozeSet(enabled: Boolean) {
lifecycleScope.launchWhenCreated {
setupAccountsList()
}
}
private fun onDrawerStaticItemSelected(type: Type) {
fun onSignOutSelected() {
@ -493,10 +371,6 @@ abstract class NavigationActivity :
putExtra(EXTRA_CURRENT_MAILBOX_LABEL_ID, currentLabelId)
startActivity(this)
}
Type.ACCOUNT_MANAGER -> startActivityForResult(
AppUtil.decorInAppIntent(Intent(this, AccountManagerActivity::class.java)),
REQUEST_CODE_ACCOUNT_MANAGER
)
Type.INBOX -> onInbox(type.drawerOptionType)
Type.ARCHIVE, Type.STARRED, Type.DRAFTS, Type.SENT, Type.TRASH, Type.SPAM, Type.ALLMAIL ->
onOtherMailBox(type.drawerOptionType)

View File

@ -62,7 +62,6 @@ public class QuickSnoozeDialogFragment
@BindView(R.id.quick_snooze_turn_off)
TextView mQuickSnoozeTurnOff;
private QuickSnoozeListener mQuickSnoozeListener;
private List<String> mQuickSnoozeValues;
private QuickSnoozeOptionAdapter mAdapter;
private int mSelectedSnoozeMinutes = 0;
@ -81,17 +80,6 @@ public class QuickSnoozeDialogFragment
return new QuickSnoozeDialogFragment();
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mQuickSnoozeListener = (QuickSnoozeListener) context;
} catch (ClassCastException e) {
// not throwing error, since the mUser of this mCustomValueDialog is not obligated to listen for
// labels state change
}
}
@Override
protected int getLayoutResourceId() {
return R.layout.dialog_fragment_quick_snooze;
@ -154,7 +142,6 @@ public class QuickSnoozeDialogFragment
}
mSelectedSnoozeMinutes = getResources().getIntArray(R.array.quick_snooze_values_int)[position];
userManager.setSnoozeQuickBlocking(true, mSelectedSnoozeMinutes);
mQuickSnoozeListener.onQuickSnoozeSet(true);
dismissAllowingStateLoss();
}
@ -162,7 +149,6 @@ public class QuickSnoozeDialogFragment
public void onQuickSnoozeTurnOffClicked() {
mSelectedSnoozeMinutes = 0;
userManager.setSnoozeQuickBlocking(false, 0);
mQuickSnoozeListener.onQuickSnoozeSet(false);
dismissAllowingStateLoss();
}
@ -170,7 +156,6 @@ public class QuickSnoozeDialogFragment
public void onCustomQuickSnoozeSet(int minutes) {
mSelectedSnoozeMinutes = minutes;
userManager.setSnoozeQuickBlocking(true, mSelectedSnoozeMinutes);
mQuickSnoozeListener.onQuickSnoozeSet(true);
dismissAllowingStateLoss();
}
@ -178,8 +163,4 @@ public class QuickSnoozeDialogFragment
public void onCancel() {
mCustomValueDialog.dismissAllowingStateLoss();
}
public interface QuickSnoozeListener {
void onQuickSnoozeSet(boolean enabled);
}
}

View File

@ -1,157 +0,0 @@
/*
* 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/.
*/
package ch.protonmail.android.activities.multiuser
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import ch.protonmail.android.R
import ch.protonmail.android.activities.BaseActivity
import ch.protonmail.android.adapters.AccountsAdapter
import ch.protonmail.android.api.models.User
import ch.protonmail.android.core.UserManager
import ch.protonmail.android.domain.entity.Id
import ch.protonmail.android.uiModel.DrawerUserModel
import ch.protonmail.android.utils.extensions.showToast
import ch.protonmail.android.utils.ui.dialogs.DialogUtils.Companion.showTwoButtonInfoDialog
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.activity_account_manager.*
import kotlinx.android.synthetic.main.toolbar_white.*
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import me.proton.core.account.domain.entity.Account
import me.proton.core.account.domain.entity.isReady
import me.proton.core.domain.entity.UserId
import javax.inject.Inject
@AndroidEntryPoint
class AccountManagerActivity : BaseActivity() {
private val accountsAdapter by lazy { AccountsAdapter() }
@Inject
lateinit var userManager: UserManager
override fun getLayoutId() = R.layout.activity_account_manager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
toolbar.setNavigationIcon(R.drawable.ic_close)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
accountsAdapter.apply {
onLoginAccount = { userId -> accountStateManager.login(userId) }
onLogoutAccount = { userId -> onLogoutClicked(userId) }
onRemoveAccount = { userId -> onRemoveClicked(userId) }
}
accountsRecyclerView.layoutManager = LinearLayoutManager(this)
accountsRecyclerView.adapter = accountsAdapter
accountStateManager.getSortedAccounts()
.distinctUntilChanged()
.flowWithLifecycle(lifecycle, Lifecycle.State.CREATED)
.onEach { sortedAccounts ->
val accounts = sortedAccounts.mapIndexed { index, account ->
val id = Id(account.userId.id)
val user = userManager.getLegacyUserOrNull(id)
account.toUiModel(account.isReady(), index == 0, user)
}
accountsAdapter.items = accounts + DrawerUserModel.AccFooter
}.launchIn(lifecycleScope)
}
private fun onRemoveClicked(userId: UserId) {
lifecycleScope.launchWhenCreated {
val account = checkNotNull(accountStateManager.getAccountOrNull(userId))
val username = account.username
showTwoButtonInfoDialog(
titleStringId = R.string.logout,
message = getString(R.string.remove_account_question, username)
) {
accountStateManager.remove(userId)
}
}
}
private fun onLogoutClicked(userId: UserId) {
lifecycleScope.launchWhenCreated {
val nextLoggedInUserId = userManager.getPreviousCurrentUserId()
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.currentUser)
getString(R.string.log_out, current.name.s) to getString(R.string.logout_question)
}
showTwoButtonInfoDialog(title = title, message = message) {
accountStateManager.logout(userId)
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.account_manager_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
saveLastInteraction()
finish()
true
}
R.id.action_remove_all -> {
showToast(R.string.account_manager_remove_all_accounts)
accountStateManager.removeAll()
finish()
true
}
else -> {
super.onOptionsItemSelected(item)
}
}
}
override fun onBackPressed() {
saveLastInteraction()
super.onBackPressed()
}
private fun Account.toUiModel(
loggedIn: Boolean,
currentPrimary: Boolean,
user: User?
) = DrawerUserModel.BaseUser.AccountUser(
id = userId,
name = username,
emailAddress = email ?: user?.defaultAddressEmail ?: username,
loggedIn = loggedIn,
primary = currentPrimary,
displayName = user?.displayName ?: username
)
}

View File

@ -1,238 +0,0 @@
/*
* 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/.
*/
package ch.protonmail.android.adapters
import android.app.Activity
import android.content.Intent
import android.graphics.Typeface
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu
import androidx.core.app.ActivityCompat.startActivityForResult
import ch.protonmail.android.R
import ch.protonmail.android.activities.REQUEST_CODE_ACCOUNT_MANAGER
import ch.protonmail.android.activities.multiuser.AccountManagerActivity
import ch.protonmail.android.uiModel.DrawerUserModel
import ch.protonmail.android.utils.AppUtil
import ch.protonmail.android.utils.extensions.setAccountLetters
import ch.protonmail.android.utils.extensions.setNotificationIndicatorSize
import ch.protonmail.android.utils.extensions.setStyle
import ch.protonmail.libs.core.ui.adapter.BaseAdapter
import ch.protonmail.libs.core.ui.adapter.ClickableAdapter
import kotlinx.android.synthetic.main.drawer_user_list_item.view.*
import kotlinx.android.synthetic.main.drawer_user_list_item_footer.view.*
import kotlinx.android.synthetic.main.user_list_item.view.*
import kotlinx.android.synthetic.main.user_list_item_footer.view.*
import me.proton.core.domain.entity.UserId
import me.proton.core.presentation.ui.adapter.ProtonAdapter
import me.proton.core.presentation.utils.inflate
// region constants
private const val VIEW_TYPE_NAV_USER = 0 // for user list item in nav drawer list
private const val VIEW_TYPE_DIVIDER = 1 // for divider in every screen
private const val VIEW_TYPE_NAV_FOOTER = 2 // for footer list item in nav drawer list
private const val VIEW_TYPE_ACC_USER = 3 // for user list item in accounts manager screen
private const val VIEW_TYPE_ACC_FOOTER = 4 // for footer list item in accounts manager screen
// endregion
/**
* Adapter for Drawer Users drop-down (Spinner)
*
* Inherits from [BaseAdapter]
* TODO Inherit from [ProtonAdapter]
*
* TODO split to DrawerAccountsAdapter and AccountManagerAccountAdapter
*/
internal class AccountsAdapter :
BaseAdapter<DrawerUserModel, AccountsAdapter.ViewHolder<DrawerUserModel>>(ModelsComparator) {
var onLoginAccount: (UserId?) -> Unit = { }
var onLogoutAccount: (UserId) -> Unit = { }
var onRemoveAccount: (UserId) -> Unit = { }
private val onLoginAccountInvoker: (UserId?) -> Unit get() = { onLoginAccount(it) }
private val onLogoutAccountInvoker: (UserId) -> Unit get() = { onLogoutAccount(it) }
private val onRemoveAccountInvoker: (UserId) -> Unit get() = { onRemoveAccount(it) }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<DrawerUserModel> =
parent.viewHolderForViewType(viewType)
override fun onBindViewHolder(holder: ViewHolder<DrawerUserModel>, position: Int) {
super.onBindViewHolder(holder, position)
holder.onLoginAccountInvoker = this.onLoginAccountInvoker
holder.onLogoutAccountInvoker = this.onLogoutAccountInvoker
holder.onRemoveAccountInvoker = this.onRemoveAccountInvoker
}
/** @return [Int] that identifies the View type for the Item at the given [position] */
override fun getItemViewType(position: Int) = items[position].viewType
abstract class ViewHolder<DUM : DrawerUserModel>(itemView: View) : ClickableAdapter.ViewHolder<DUM>(itemView) {
internal var onLoginAccountInvoker: (UserId?) -> Unit = { }
internal var onLogoutAccountInvoker: (UserId) -> Unit = { }
internal var onRemoveAccountInvoker: (UserId) -> Unit = { }
}
/** A [BaseAdapter.ItemsComparator] for the Adapter */
private object ModelsComparator : BaseAdapter.ItemsComparator<DrawerUserModel>() {
override fun areItemsTheSame(oldItem: DrawerUserModel, newItem: DrawerUserModel): Boolean =
false
}
/** @return [LayoutRes] for the given [viewType] */
private fun layoutForViewType(viewType: Int) = when (viewType) {
VIEW_TYPE_DIVIDER -> R.layout.drawer_list_item_divider
VIEW_TYPE_NAV_FOOTER -> R.layout.drawer_user_list_item_footer
VIEW_TYPE_ACC_FOOTER -> R.layout.user_list_item_footer
VIEW_TYPE_NAV_USER -> R.layout.drawer_user_list_item
VIEW_TYPE_ACC_USER -> R.layout.user_list_item
else -> throw IllegalArgumentException("View type not found: '$viewType'")
}
// region extension functions
private fun <DUM : DrawerUserModel> ViewGroup.viewHolderForViewType(viewType: Int): ViewHolder<DUM> {
val view = inflate(layoutForViewType(viewType))
@Suppress("UNCHECKED_CAST") // Type cannot be checked since is in invariant position
return when (viewType) {
VIEW_TYPE_DIVIDER -> DividerViewHolder(view)
VIEW_TYPE_NAV_FOOTER -> FooterViewHolder(view)
VIEW_TYPE_NAV_USER -> NavUserViewHolder(view)
VIEW_TYPE_ACC_USER -> AccUserViewHolder(view)
VIEW_TYPE_ACC_FOOTER -> AccFooterViewHolder(view)
else -> throw IllegalArgumentException("View type not found: '$viewType'")
} as ViewHolder<DUM>
}
/** @return [Int] view type for the receiver [DrawerUserModel] */
private val DrawerUserModel.viewType: Int
get() {
return when (this) {
is DrawerUserModel.Footer -> VIEW_TYPE_NAV_FOOTER
is DrawerUserModel.Divider -> VIEW_TYPE_DIVIDER
is DrawerUserModel.BaseUser.DrawerUser -> VIEW_TYPE_NAV_USER
is DrawerUserModel.BaseUser.AccountUser -> VIEW_TYPE_ACC_USER
is DrawerUserModel.AccFooter -> VIEW_TYPE_ACC_FOOTER
}
}
// endregion
// region view holders
private class DividerViewHolder(itemView: View) : ViewHolder<DrawerUserModel.Divider>(itemView)
private class AccFooterViewHolder(itemView: View) : ViewHolder<DrawerUserModel.AccFooter>(itemView) {
override fun onBind(item: DrawerUserModel.AccFooter) = with(itemView) {
super.onBind(item)
addNewUserAccount.setOnClickListener {
onLoginAccountInvoker.invoke(null)
}
}
}
private class FooterViewHolder(itemView: View) : ViewHolder<DrawerUserModel.Footer>(itemView) {
override fun onBind(item: DrawerUserModel.Footer) = with(itemView) {
super.onBind(item)
manageAccounts.setOnClickListener {
val activity = context as Activity
startActivityForResult(
activity,
AppUtil.decorInAppIntent(Intent(context, AccountManagerActivity::class.java)),
REQUEST_CODE_ACCOUNT_MANAGER,
null
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.slide_up_close)
}
}
}
private class AccUserViewHolder(itemView: View) : ViewHolder<DrawerUserModel.BaseUser.AccountUser>(itemView) {
override fun onBind(item: DrawerUserModel.BaseUser.AccountUser) = with(itemView) {
super.onBind(item)
accUserName.text = item.displayName
accUserEmailAddress.text = item.emailAddress
accUserEmailAddress.visibility = if (item.emailAddress.isEmpty()) View.GONE else View.VISIBLE
accUserAvatar.setAccountLetters(item.displayName)
if (!item.loggedIn) {
accUserName.text = String.format(context.getString(R.string.manage_accounts_user_loggedout), item.name)
accUserName.setStyle(R.style.DrawerNameText_Red)
accUserEmailAddress.setStyle(R.style.DrawerEmailAddressText_Red)
} else if (item.primary) {
accUserName.text = String.format(context.getString(R.string.manage_accounts_user_primary), item.displayName)
accUserName.setTypeface(accUserName.typeface, Typeface.BOLD_ITALIC)
accUserEmailAddress.setTypeface(accUserEmailAddress.typeface, Typeface.BOLD_ITALIC)
}
accUserMoreMenu.setOnClickListener {
val popupMenu = PopupMenu(itemView.context, accUserMoreMenu)
popupMenu.let {
it.menuInflater.inflate(
if (item.loggedIn) R.menu.account_item_menu
else R.menu.account_item_loggedout_menu, it.menu
)
it.gravity = Gravity.END
}
popupMenu.setOnMenuItemClickListener {
when (it.itemId) {
R.id.action_remove_account -> {
onRemoveAccountInvoker(item.id)
}
R.id.action_logout_account -> {
onLogoutAccountInvoker(item.id)
}
R.id.action_login -> {
onLoginAccountInvoker(item.id)
}
}
true
}
popupMenu.show()
}
}
}
private class NavUserViewHolder(itemView: View) : ViewHolder<DrawerUserModel.BaseUser.DrawerUser>(itemView) {
override fun onBind(item: DrawerUserModel.BaseUser.DrawerUser) = with(itemView) {
super.onBind(item)
userName.text = item.displayName
userEmailAddress.text = item.emailAddress
userEmailAddress.visibility =
if (item.emailAddress.isEmpty()) View.GONE else View.VISIBLE
userAvatar.setAccountLetters(item.displayName)
if (item.notificationsSnoozed) {
buttonUserQuickSnooze.setImageResource(R.drawable.ic_notifications_off)
} else {
buttonUserQuickSnooze.setImageResource(R.drawable.ic_notifications_active)
}
if (item.loggedIn) {
userLoginStatusParent.visibility = View.GONE
userSignedIn.visibility = View.VISIBLE
userNotifications.text = "${item.notifications}"
userNotifications.setNotificationIndicatorSize(item.notifications)
userNotifications.visibility = if (item.notifications == 0) View.GONE else View.VISIBLE
} else {
userLoginStatusParent.visibility = View.VISIBLE
userName.setStyle(R.style.DrawerNameText_Red)
userEmailAddress.setStyle(R.style.DrawerEmailAddressText_Red)
}
}
}
// endregion
}

View File

@ -163,10 +163,6 @@ class UserManager @Inject constructor(
}
}
suspend fun getLegacyUserOrNull(userId: Id): User? = runCatching {
getLegacyUser(userId)
}.getOrNull()
fun getLegacyUserBlocking(userId: Id) = runBlocking {
getLegacyUser(userId)
}

View File

@ -24,6 +24,7 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import me.proton.core.account.domain.entity.AccountType
import me.proton.core.domain.entity.Product
import javax.inject.Singleton
@ -35,6 +36,10 @@ object CoreAppModule {
@Singleton
fun provideProduct(): Product = Product.Mail
@Provides
@Singleton
fun provideRequiredAccountType(): AccountType = AccountType.Internal
@Provides
@Singleton
@AppProcessLifecycleOwner

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2021 Proton Technologies AG
* This file is part of Proton Technologies AG and ProtonCore.
*
* ProtonCore 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.
*
* ProtonCore 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 ProtonCore. If not, see <https://www.gnu.org/licenses/>.
*/
package ch.protonmail.android.di
import android.content.Context
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import me.proton.core.country.data.repository.CountriesRepositoryImpl
import me.proton.core.country.domain.repository.CountriesRepository
@Module
@InstallIn(SingletonComponent::class)
class CoreCountriesModule {
@Provides
fun provideCountriesRepository(@ApplicationContext context: Context): CountriesRepository =
CountriesRepositoryImpl(context)
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2021 Proton Technologies AG
* This file is part of Proton Technologies AG and ProtonCore.
*
* ProtonCore 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.
*
* ProtonCore 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 ProtonCore. If not, see <https://www.gnu.org/licenses/>.
*/
package ch.protonmail.android.di
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import me.proton.core.accountmanager.data.db.AccountManagerDatabase
import me.proton.core.crypto.common.keystore.KeyStoreCrypto
import me.proton.core.humanverification.data.HumanVerificationListenerImpl
import me.proton.core.humanverification.data.HumanVerificationManagerImpl
import me.proton.core.humanverification.data.repository.HumanVerificationRepositoryImpl
import me.proton.core.humanverification.domain.HumanVerificationManager
import me.proton.core.humanverification.domain.HumanVerificationWorkflowHandler
import me.proton.core.humanverification.domain.repository.HumanVerificationRepository
import me.proton.core.network.data.ApiProvider
import me.proton.core.network.domain.session.HumanVerificationListener
import me.proton.core.network.domain.session.HumanVerificationProvider
import me.proton.core.user.data.repository.UserValidationRepositoryImpl
import me.proton.core.user.domain.repository.UserValidationRepository
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object HumanVerificationModule {
@Provides
@Singleton
fun provideHumanVerificationListener(
humanVerificationRepository: HumanVerificationRepository
): HumanVerificationListener =
HumanVerificationListenerImpl(humanVerificationRepository)
@Provides
@Singleton
fun provideHumanVerificationRepository(
db: AccountManagerDatabase,
keyStoreCrypto: KeyStoreCrypto
): HumanVerificationRepository =
HumanVerificationRepositoryImpl(db, keyStoreCrypto)
@Provides
@Singleton
fun provideHumanVerificationManager(
humanVerificationRepository: HumanVerificationRepository
): HumanVerificationManagerImpl = HumanVerificationManagerImpl(humanVerificationRepository)
@Provides
@Singleton
fun provideUserValidationRepositoryImpl(
provider: ApiProvider,
humanVerificationListener: HumanVerificationListener
): UserValidationRepository = UserValidationRepositoryImpl(provider, humanVerificationListener)
}
@Module
@InstallIn(SingletonComponent::class)
interface HumanVerificationBindModule {
@Binds
fun bindHumanVerificationManager(
humanVerificationManagerImpl: HumanVerificationManagerImpl
): HumanVerificationManager
@Binds
fun bindHumanVerificationWorkflowHandler(
humanVerificationManagerImpl: HumanVerificationManagerImpl
): HumanVerificationWorkflowHandler
@Binds
fun bindHumanVerificationProvider(
humanVerificationManagerImpl: HumanVerificationManagerImpl
): HumanVerificationProvider
}

View File

@ -15,12 +15,15 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import me.proton.core.auth.domain.ClientSecret
import me.proton.core.network.data.ApiProvider
import me.proton.core.network.data.ProtonCookieStore
import me.proton.core.network.data.di.ApiFactory
import me.proton.core.network.data.di.NetworkManager
import me.proton.core.network.data.di.NetworkPrefs
import me.proton.core.network.domain.ApiClient
import me.proton.core.network.domain.NetworkManager
import me.proton.core.network.domain.NetworkPrefs
import me.proton.core.network.domain.session.HumanVerificationListener
import me.proton.core.network.domain.session.HumanVerificationProvider
import me.proton.core.network.domain.session.SessionListener
import me.proton.core.network.domain.session.SessionProvider
import me.proton.core.util.kotlin.Logger
@ -51,12 +54,15 @@ object NetworkModule {
@Provides
@Singleton
fun provideApiFactory(
@ApplicationContext context: Context,
logger: Logger,
apiClient: ApiClient,
networkManager: NetworkManager,
networkPrefs: NetworkPrefs,
sessionProvider: SessionProvider,
sessionListener: SessionListener
sessionListener: SessionListener,
humanVerificationProvider: HumanVerificationProvider,
humanVerificationListener: HumanVerificationListener
): ApiFactory = ApiFactory(
Constants.ENDPOINT_URI,
apiClient,
@ -64,9 +70,11 @@ object NetworkModule {
networkManager,
networkPrefs,
sessionProvider,
humanVerificationProvider,
sessionListener,
cookieStore = null,
scope = CoroutineScope(Job() + Dispatchers.Default)
humanVerificationListener,
ProtonCookieStore(context),
CoroutineScope(Job() + Dispatchers.Default)
)
@Provides

View File

@ -44,7 +44,6 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.launch
@ -63,7 +62,6 @@ import me.proton.core.accountmanager.presentation.onAccountReady
import me.proton.core.accountmanager.presentation.onAccountRemoved
import me.proton.core.accountmanager.presentation.onAccountTwoPassModeFailed
import me.proton.core.accountmanager.presentation.onAccountTwoPassModeNeeded
import me.proton.core.accountmanager.presentation.onSessionHumanVerificationNeeded
import me.proton.core.accountmanager.presentation.onSessionSecondFactorNeeded
import me.proton.core.auth.presentation.AuthOrchestrator
import me.proton.core.auth.presentation.onLoginResult
@ -75,6 +73,7 @@ import javax.inject.Singleton
@Singleton
class AccountStateManager @Inject constructor(
private val requiredAccountType: AccountType,
private val accountManager: AccountManager,
private val userManager: UserManager,
private var authOrchestrator: AuthOrchestrator,
@ -107,7 +106,6 @@ class AccountStateManager @Inject constructor(
with(authOrchestrator) {
// Observe all Accounts States (need a registered authOrchestrator, see register).
accountManager.observe(lifecycle, minActiveState = Lifecycle.State.CREATED)
.onSessionHumanVerificationNeeded { startHumanVerificationWorkflow(it) }
.onSessionSecondFactorNeeded { startSecondFactorWorkflow(it) }
.onAccountTwoPassModeNeeded { startTwoPassModeWorkflow(it) }
.onAccountCreateAddressNeeded { startChooseAddressWorkflow(it) }
@ -148,14 +146,6 @@ class AccountStateManager @Inject constructor(
suspend fun getAccountOrNull(userId: UserId) = getAccount(userId).firstOrNull()
/* Order: Primary account, ready account(s), other account(s). */
fun getSortedAccounts() = accountManager.getAccounts().mapLatest { accounts ->
val currentUser = getPrimaryUserId().firstOrNull()
accounts
.sortedByDescending { it.userId == currentUser }
.sortedByDescending { it.isReady() }
}
fun logout(userId: UserId) = scope.launch {
userManager.lock(userId)
accountManager.disableAccount(userId)
@ -168,7 +158,7 @@ class AccountStateManager @Inject constructor(
fun login(userId: UserId? = null) = scope.launch {
val account = userId?.let { getAccountOrNull(it) }
authOrchestrator.startLoginWorkflow(
requiredAccountType = AccountType.Internal,
requiredAccountType = requiredAccountType,
username = account?.username
)
}
@ -184,16 +174,6 @@ class AccountStateManager @Inject constructor(
}
}
fun remove(userId: UserId) = scope.launch {
accountManager.removeAccount(userId)
}
fun removeAll() = scope.launch {
accountManager.getAccounts().first().forEach {
accountManager.removeAccount(it.userId)
}
}
data class AccountSwitch(val previous: Account? = null, val current: Account? = null)
fun onAccountSwitched() = getPrimaryUserId().scan(AccountSwitch()) { previous, currentUserId ->
@ -214,12 +194,6 @@ class AccountStateManager @Inject constructor(
// region Deprecated
@Deprecated(
message = "Use UserId version of the function",
replaceWith = ReplaceWith("getAccountOrNull(UserId(userId.s))", "me.proton.core.domain.entity.UserId")
)
suspend fun getAccountOrNull(userId: Id) = getAccountOrNull(UserId(userId.s))
@Deprecated(
message = "Use UserId version of the function",
replaceWith = ReplaceWith("switch(UserId(userId.s))", "me.proton.core.domain.entity.UserId")
@ -232,12 +206,6 @@ class AccountStateManager @Inject constructor(
)
fun logout(userId: Id) = logout(UserId(userId.s))
@Deprecated(
message = "Use UserId version of the function",
replaceWith = ReplaceWith("remove(UserId(userId.s))", "me.proton.core.domain.entity.UserId")
)
fun remove(userId: Id) = remove(UserId(userId.s))
// endregion
// region Legacy User Management & Cleaning.

View File

@ -97,7 +97,6 @@ class CoreAccountManagerMigration @Inject constructor(
sessionId = SessionId(requireNotBlank(tokenManager.sessionId)),
accessToken = requireNotBlank(tokenManager.accessToken),
refreshToken = requireNotBlank(tokenManager.refreshToken),
headers = null,
scopes = tokenManager.scope.split(" ")
)
val account = Account(
@ -109,7 +108,6 @@ class CoreAccountManagerMigration @Inject constructor(
sessionState = SessionState.Authenticated,
details = AccountDetails(
session = null,
humanVerification = null
)
)
Migration(account, session, requireNotBlank(passphrase))

View File

@ -543,7 +543,6 @@ class MailboxActivity :
AppUtil.clearNotifications(this, currentUserId)
lazyManager.reset()
setUpDrawer()
setupAccountsList()
checkRegistration()
// Loading mailbox items for the newly switched account.
// This method also "reloads dependencies" for the instance of `messageDetailsRepo` held by
@ -949,10 +948,7 @@ class MailboxActivity :
@Subscribe
fun onSettingsChangedEvent(event: SettingsChangedEvent) {
val user = userManager.requireCurrentUser()
if (event.success) {
refreshDrawerHeader(user)
} else {
if (!event.success) {
showToast(R.string.saving_failed_no_conn, Toast.LENGTH_LONG, Gravity.CENTER)
}
}
@ -1008,11 +1004,7 @@ class MailboxActivity :
}
@Subscribe
fun onUpdatesLoaded(event: FetchUpdatesEvent?) {
lifecycleScope.launchWhenCreated {
userManager.currentUser?.let { refreshDrawerHeader(it) }
}
}
fun onUpdatesLoaded(event: FetchUpdatesEvent?) { }
private fun showToast(status: Status) {
when (status) {

View File

@ -1,60 +0,0 @@
/*
* 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/.
*/
package ch.protonmail.android.ui.view
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import android.widget.TextView
import androidx.annotation.StyleRes
import ch.protonmail.android.databinding.LayoutDrawerHeaderBinding
import ch.protonmail.android.domain.entity.EmailAddress
import ch.protonmail.android.domain.entity.Name
internal class ProtonDrawerHeader @JvmOverloads constructor (
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
@StyleRes defStyleRes: Int = 0
) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
private val initialsTextView: TextView
private val nameTextView: TextView
private val emailTextView: TextView
init {
val binding = LayoutDrawerHeaderBinding.inflate(
LayoutInflater.from(context),
this,
true
)
initialsTextView = binding.drawerHeaderInitialsTextView
nameTextView = binding.drawerHeaderNameTextView
emailTextView = binding.drawerHeaderEmailTextView
}
fun setUser(initials: Pair<Char, Char>, name: Name, email: EmailAddress) {
val stringInitials = "${initials.first}${initials.second}"
initialsTextView.text = stringInitials
nameTextView.text = name.s
emailTextView.text = email.s
}
}

View File

@ -27,14 +27,10 @@ import androidx.annotation.StringRes
import androidx.annotation.StyleRes
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import ch.protonmail.android.R
import ch.protonmail.android.adapters.DrawerAdapter
import ch.protonmail.android.domain.entity.EmailAddress
import ch.protonmail.android.domain.entity.Name
import ch.protonmail.android.uiModel.DrawerItemUiModel
import ch.protonmail.libs.core.utils.onClick
internal class ProtonSideDrawer @JvmOverloads constructor (
internal class ProtonSideDrawer @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
@ -42,7 +38,6 @@ internal class ProtonSideDrawer @JvmOverloads constructor (
) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
private val bodyRecyclerView: RecyclerView
private val headerView = ProtonDrawerHeader(context)
private val bodyAdapter = DrawerAdapter()
// lists
@ -56,8 +51,6 @@ internal class ProtonSideDrawer @JvmOverloads constructor (
private var footerItem: DrawerItemUiModel.Footer? = null
init {
setBackgroundResource(R.color.nav_view_background)
bodyRecyclerView = RecyclerView(context).apply {
layoutManager = LinearLayoutManager(context)
adapter = bodyAdapter
@ -65,18 +58,12 @@ internal class ProtonSideDrawer @JvmOverloads constructor (
val linearLayout = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
addView(headerView)
addView(bodyRecyclerView)
}
addView(linearLayout)
}
fun setOnHeaderUserClick(block: () -> Unit) {
headerView.onClick(block)
}
fun setOnItemClick(block: (DrawerItemUiModel) -> Unit) {
bodyAdapter.onItemClick = { drawerItemUiModel ->
block(drawerItemUiModel)
@ -85,10 +72,6 @@ internal class ProtonSideDrawer @JvmOverloads constructor (
}
}
fun setUser(initials: Pair<Char, Char>, name: Name, email: EmailAddress) {
headerView.setUser(initials, name, email)
}
fun setLocationItems(items: List<DrawerItemUiModel.Primary.Static>) {
locationItems = items
update()

View File

@ -116,7 +116,6 @@ internal sealed class DrawerItemUiModel {
REPORT_BUGS(101, DrawerOptionType.REPORT_BUGS),
SIGNOUT(111, DrawerOptionType.SIGN_OUT),
LOCK(112, DrawerOptionType.LOCK),
ACCOUNT_MANAGER(115, DrawerOptionType.ACCOUNT_MANAGER)
}
}

View File

@ -1,84 +0,0 @@
/*
* 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/.
*/
package ch.protonmail.android.uiModel
import me.proton.core.domain.entity.UserId
import me.proton.core.util.kotlin.EMPTY_STRING
/**
* Represent a base Navigation Drawer User data class.
* @see ch.protonmail.android.adapters.AccountsAdapter
*
* TODO split to DrawerUserModel and AccountManagerUserModel
*/
internal sealed class DrawerUserModel {
sealed class BaseUser : DrawerUserModel() {
abstract val id: UserId
/**
* User's user name. Default is empty
*/
open val name: String = EMPTY_STRING
/**
* User's email. Default is empty.
*/
open val emailAddress: String = EMPTY_STRING
/**
* Value of whether this user is currently logged in or it is logged out.
*/
open val loggedIn: Boolean = false
/**
* Value of whether this user has snoozed his notifications at the moment.
*/
open val notificationsSnoozed: Boolean = false
data class AccountUser(
override val id: UserId,
override val name: String,
override val emailAddress: String,
override val loggedIn: Boolean,
val primary: Boolean,
val displayName: String
) : BaseUser()
data class DrawerUser(
override val id: UserId,
override val name: String,
override val emailAddress: String,
override val loggedIn: Boolean,
val notifications: Int,
override val notificationsSnoozed: Boolean,
val displayName: String
) : BaseUser()
}
/** Divider for Drawer Items */
object Divider : DrawerUserModel()
/** Footer for Nav Drawer Items */
object Footer : DrawerUserModel()
/** Footer for Account Manager Items */
object AccFooter : DrawerUserModel()
}

View File

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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/.
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include
android:id="@+id/toolbar_parent"
layout="@layout/toolbar_white" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/accountsRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2020 Proton Technologies AG
~
~ This file is part of ProtonMail.
@ -18,25 +17,27 @@
~ along with ProtonMail. If not, see https://www.gnu.org/licenses/.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/navdrawer_width"
android:layout_height="match_parent"
android:layout_gravity="start">
android:layout_gravity="start"
android:background="@color/nav_view_background"
android:orientation="vertical">
<me.proton.core.accountmanager.presentation.view.AccountPrimaryView
android:id="@+id/accountPrimaryView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/gap_medium"
tools:email="superuser@protonmail.com"
tools:initials="SU"
tools:isExpandable="true"
tools:name="Super User" />
<ch.protonmail.android.ui.view.ProtonSideDrawer
android:id="@+id/sideDrawer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/left_drawer_users"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:background="@color/white"
android:visibility="gone"/>
</FrameLayout>
</LinearLayout>

View File

@ -1,99 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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/.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/white">
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_alignParentBottom="true"
android:background="@color/iron_gray" />
<RelativeLayout
android:id="@+id/accItem"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/divider"
android:paddingStart="@dimen/fields_default_space_medium"
android:paddingTop="@dimen/fields_default_space_small"
android:paddingEnd="@dimen/fields_default_space"
android:paddingBottom="@dimen/fields_default_space_small">
<!-- Icon -->
<ch.protonmail.android.views.contactsList.ContactGroupEmailAvatarView
android:id="@+id/accUserAvatar"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_centerVertical="true"
android:gravity="center" />
<ImageButton
android:id="@+id/accUserMoreMenu"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_gravity="center"
android:layout_centerInParent="true"
android:background="@android:color/transparent"
android:tint="@color/lead_gray"
app:srcCompat="@drawable/ic_more"
tools:tint="@color/lead_gray" />
<RelativeLayout
android:id="@+id/userDetailsParent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="@dimen/fields_default_space"
android:layout_marginEnd="@dimen/fields_default_space"
android:layout_toStartOf="@id/accUserMoreMenu"
android:layout_toEndOf="@id/accUserAvatar"
android:gravity="center_vertical">
<!-- Name -->
<TextView
android:id="@+id/accUserName"
style="@style/DrawerNameText.Black"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/accUserEmailAddress"
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
tools:text="@tools:sample/full_names" />
<!-- Email -->
<TextView
android:id="@+id/accUserEmailAddress"
style="@style/DrawerEmailAddressText.Black"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
tools:text="exampleemail@proton.com" />
</RelativeLayout>
</RelativeLayout>
</RelativeLayout>

View File

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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/.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<ch.protonmail.android.views.CustomFontTextView
android:id="@+id/addNewUserAccount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:layout_marginTop="5dp"
android:text="@string/account_manager_add_account"
android:textAllCaps="false"
android:textColor="@color/new_purple_dark"
android:textColorLink="@color/new_purple_dark"
android:textSize="@dimen/h3"
app:fontName="Roboto-Light.ttf" />
<ch.protonmail.android.views.CustomFontTextView
style="@style/SettingsText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
android:text="@string/account_manager_note"
app:fontName="Roboto-Light.ttf" />
</LinearLayout>

View File

@ -41,6 +41,7 @@ val DependencyHandler.`Proton-account-manager` get() = proton("acco
val DependencyHandler.`Proton-user` get() = proton("user") version `Proton-user version`
val DependencyHandler.`Proton-key` get() = proton("key") version `Proton-key version`
val DependencyHandler.`Proton-human-verification` get() = proton("human-verification") version `Proton-human-verification version`
val DependencyHandler.`Proton-country` get() = proton("country") version `Proton-country version`
fun DependencyHandler.protonTest(moduleSuffix: String, version: String? = null) =
proton("test", moduleSuffix, version)
@ -58,7 +59,6 @@ val DependencyHandler.`android-media` get() = androidx("media") ve
val DependencyHandler.`android-flexbox` get() = google("android", "flexbox") version `flexbox version`
val DependencyHandler.`android-preference` get() = androidx("preference", moduleSuffix = "ktx") version `android-preference version`
val DependencyHandler.`google-services` get() = googleServices()
val DependencyHandler.`hilt-androidx-view-model` get() = dependency("androidx.hilt", module = "hilt-lifecycle-viewmodel") version `hilt-androidx-viewmodel version`
val DependencyHandler.`room-rxJava` get() = androidxRoom("rxjava2")
val DependencyHandler.`safetyNet` get() = playServices("safetynet")
val DependencyHandler.`lifecycle-extensions` get() = androidxLifecycle("extensions") version `lifecycle-extensions version`

View File

@ -36,8 +36,8 @@ fun initVersions() {
`android-arch version` = "2.1.0" // Released: Sep 06, 2019
`constraint-layout version` = "2.0.0-rc1" // Released: Jul 30, 2020
`espresso version` = "3.4.0-alpha05" // Released: Mar 15, 2021
`hilt-android version` = "2.33-beta" // Released: Feb 26, 2021
`hilt-androidx version` = "1.0.0-beta01" // Released: Mar 10, 2021
`hilt-android version` = "2.35.1" // Released: Apr 28, 2021
`hilt-androidx version` = "1.0.0" // Released: May 05, 2021
`ktx version` = "1.5.0-rc01" // Released: Mar 24, 2021
`lifecycle version` = "2.4.0-alpha01" // Released: Mar 24, 2021
`material version` = "1.3.0" // Released: Feb 04, 2021
@ -51,7 +51,7 @@ fun initVersions() {
// region Others
`assert4k version` = "0.7.1" // Released: May 04, 2021
`assistedInject version` = "0.6.0" // Released: Sep 14, 2020
`dagger version` = "2.28.3" // Released: Jul 18, 2020
`dagger version` = "2.35.1" // Released: Apr 28, 2021
`mockK version` = "1.10.0" // Released: Apr 19, 2020
`retrofit version` = "2.6.1" // Released: Jul 31, 2019
@ -72,17 +72,17 @@ const val `Proton-android-instr-test version` = "0.3.3" // Released: Mar
const val `Proton-kotlin-test version` = "0.2" // Released: Oct 21, 2020
const val `Proton-domain version` = "1.0" // Released: Feb 17, 2021
const val `Proton-presentation version` = "0.7.4" // Released: May 05, 2021
const val `Proton-presentation version` = "0.7.8" // Released: May 12, 2021
const val `Proton-data version` = "1.0.3" // Released: Mar 22, 2021
const val `Proton-network version` = "1.0.4" // Released: Apr 07, 2021
const val `Proton-network version` = "1.1" // Released: May 12, 2021
const val `Proton-human-verification version` = "1.1.1" // Released: May 12, 2021
const val `Proton-crypto version` = "1.0.2" // Released: Mar 05, 2021
const val `Proton-auth version` = "1.0.8" // Released: May 10, 2021
const val `Proton-account version` = "1.0.3" // Released: Apr 12, 2021
const val `Proton-account-manager version` = "1.0.9" // Released: May 03, 2021
const val `Proton-user version` = "1.0.6" // Released: May 03, 2021
const val `Proton-country version` = "0.1.4" // Released: May 12, 2021
const val `Proton-auth version` = "1.1.1" // Released: May 12, 2021
const val `Proton-account version` = "1.1" // Released: May 12, 2021
const val `Proton-account-manager version` = "1.1.1" // Released: May 12, 2021
const val `Proton-user version` = "1.1.1" // Released: May 12, 2021
const val `Proton-key version` = "1.0.4" // Released: Mar 22, 2021
const val `Proton-human-verification version` = "0.2.6" // Released: Mar 14, 2021
@Suppress("unused") const val `composer version` = "1.0-beta-3" // Released: Feb 12, 2020
@ -106,7 +106,6 @@ const val `android-media version` = "1.1.0" // Released: Sep
const val `flexbox version` = "2.0.1" // Released: Jan 17, 2020
const val `android-preference version` = "1.1.1" // Released: Apr 15, 2020
const val `googleServices version` = "4.3.3" // Released: Nov 11, 2019
const val `hilt-androidx-viewmodel version` = "1.0.0-alpha03" // Released: Mar 10, 2021
const val `playServices version` = "17.0.0" // Released: Jun 19, 2019
const val `lifecycle-extensions version` = "2.2.0" // Released: Jan 00, 2020