Replaced Account Switcher by Core AccountPrimaryView.
Upgraded Dagger/Hilt to version 2.35.1.
This commit is contained in:
parent
0358af085d
commit
2a9532fe93
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue