Add support for OAuth flow after settings import
This commit is contained in:
parent
94c61a7999
commit
9ae7d27e79
9 changed files with 142 additions and 30 deletions
|
@ -77,17 +77,19 @@ public class SettingsImporter {
|
||||||
public final AccountDescription original;
|
public final AccountDescription original;
|
||||||
public final AccountDescription imported;
|
public final AccountDescription imported;
|
||||||
public final boolean overwritten;
|
public final boolean overwritten;
|
||||||
|
public final boolean authorizationNeeded;
|
||||||
public final boolean incomingPasswordNeeded;
|
public final boolean incomingPasswordNeeded;
|
||||||
public final boolean outgoingPasswordNeeded;
|
public final boolean outgoingPasswordNeeded;
|
||||||
public final String incomingServerName;
|
public final String incomingServerName;
|
||||||
public final String outgoingServerName;
|
public final String outgoingServerName;
|
||||||
|
|
||||||
private AccountDescriptionPair(AccountDescription original, AccountDescription imported,
|
private AccountDescriptionPair(AccountDescription original, AccountDescription imported,
|
||||||
boolean overwritten, boolean incomingPasswordNeeded, boolean outgoingPasswordNeeded,
|
boolean overwritten, boolean authorizationNeeded, boolean incomingPasswordNeeded,
|
||||||
String incomingServerName, String outgoingServerName) {
|
boolean outgoingPasswordNeeded, String incomingServerName, String outgoingServerName) {
|
||||||
this.original = original;
|
this.original = original;
|
||||||
this.imported = imported;
|
this.imported = imported;
|
||||||
this.overwritten = overwritten;
|
this.overwritten = overwritten;
|
||||||
|
this.authorizationNeeded = authorizationNeeded;
|
||||||
this.incomingPasswordNeeded = incomingPasswordNeeded;
|
this.incomingPasswordNeeded = incomingPasswordNeeded;
|
||||||
this.outgoingPasswordNeeded = outgoingPasswordNeeded;
|
this.outgoingPasswordNeeded = outgoingPasswordNeeded;
|
||||||
this.incomingServerName = incomingServerName;
|
this.incomingServerName = incomingServerName;
|
||||||
|
@ -372,8 +374,11 @@ public class SettingsImporter {
|
||||||
|
|
||||||
String incomingServerName = incoming.host;
|
String incomingServerName = incoming.host;
|
||||||
boolean incomingPasswordNeeded = AuthType.EXTERNAL != incoming.authenticationType &&
|
boolean incomingPasswordNeeded = AuthType.EXTERNAL != incoming.authenticationType &&
|
||||||
|
AuthType.XOAUTH2 != incoming.authenticationType &&
|
||||||
(incoming.password == null || incoming.password.isEmpty());
|
(incoming.password == null || incoming.password.isEmpty());
|
||||||
|
|
||||||
|
boolean authorizationNeeded = incoming.authenticationType == AuthType.XOAUTH2;
|
||||||
|
|
||||||
String incomingServerType = ServerTypeConverter.toServerSettingsType(account.incoming.type);
|
String incomingServerType = ServerTypeConverter.toServerSettingsType(account.incoming.type);
|
||||||
if (account.outgoing == null && !incomingServerType.equals(Protocols.WEBDAV)) {
|
if (account.outgoing == null && !incomingServerType.equals(Protocols.WEBDAV)) {
|
||||||
// All account types except WebDAV need to provide outgoing server settings
|
// All account types except WebDAV need to provide outgoing server settings
|
||||||
|
@ -395,15 +400,18 @@ public class SettingsImporter {
|
||||||
*/
|
*/
|
||||||
String outgoingServerType = ServerTypeConverter.toServerSettingsType(outgoing.type);
|
String outgoingServerType = ServerTypeConverter.toServerSettingsType(outgoing.type);
|
||||||
outgoingPasswordNeeded = AuthType.EXTERNAL != outgoing.authenticationType &&
|
outgoingPasswordNeeded = AuthType.EXTERNAL != outgoing.authenticationType &&
|
||||||
|
AuthType.XOAUTH2 != outgoing.authenticationType &&
|
||||||
!outgoingServerType.equals(Protocols.WEBDAV) &&
|
!outgoingServerType.equals(Protocols.WEBDAV) &&
|
||||||
outgoing.username != null &&
|
outgoing.username != null &&
|
||||||
!outgoing.username.isEmpty() &&
|
!outgoing.username.isEmpty() &&
|
||||||
(outgoing.password == null || outgoing.password.isEmpty());
|
(outgoing.password == null || outgoing.password.isEmpty());
|
||||||
|
|
||||||
|
authorizationNeeded |= outgoing.authenticationType == AuthType.XOAUTH2;
|
||||||
|
|
||||||
outgoingServerName = outgoing.host;
|
outgoingServerName = outgoing.host;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean createAccountDisabled = incomingPasswordNeeded || outgoingPasswordNeeded;
|
boolean createAccountDisabled = incomingPasswordNeeded || outgoingPasswordNeeded || authorizationNeeded;
|
||||||
if (createAccountDisabled) {
|
if (createAccountDisabled) {
|
||||||
editor.putBoolean(accountKeyPrefix + "enabled", false);
|
editor.putBoolean(accountKeyPrefix + "enabled", false);
|
||||||
}
|
}
|
||||||
|
@ -465,7 +473,7 @@ public class SettingsImporter {
|
||||||
putString(editor, accountKeyPrefix + "messagesNotificationChannelVersion", messageNotificationChannelVersion);
|
putString(editor, accountKeyPrefix + "messagesNotificationChannelVersion", messageNotificationChannelVersion);
|
||||||
|
|
||||||
AccountDescription imported = new AccountDescription(accountName, uuid);
|
AccountDescription imported = new AccountDescription(accountName, uuid);
|
||||||
return new AccountDescriptionPair(original, imported, mergeImportedAccount,
|
return new AccountDescriptionPair(original, imported, mergeImportedAccount, authorizationNeeded,
|
||||||
incomingPasswordNeeded, outgoingPasswordNeeded, incomingServerName, outgoingServerName);
|
incomingPasswordNeeded, outgoingPasswordNeeded, incomingServerName, outgoingServerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,11 +1069,12 @@ public class SettingsImporter {
|
||||||
String type = ServerTypeConverter.toServerSettingsType(importedServer.type);
|
String type = ServerTypeConverter.toServerSettingsType(importedServer.type);
|
||||||
int port = convertPort(importedServer.port);
|
int port = convertPort(importedServer.port);
|
||||||
ConnectionSecurity connectionSecurity = convertConnectionSecurity(importedServer.connectionSecurity);
|
ConnectionSecurity connectionSecurity = convertConnectionSecurity(importedServer.connectionSecurity);
|
||||||
|
String password = importedServer.authenticationType == AuthType.XOAUTH2 ? "" : importedServer.password;
|
||||||
Map<String, String> extra = importedServer.extras != null ?
|
Map<String, String> extra = importedServer.extras != null ?
|
||||||
unmodifiableMap(importedServer.extras.settings) : emptyMap();
|
unmodifiableMap(importedServer.extras.settings) : emptyMap();
|
||||||
|
|
||||||
return new ServerSettings(type, importedServer.host, port, connectionSecurity,
|
return new ServerSettings(type, importedServer.host, port, connectionSecurity,
|
||||||
importedServer.authenticationType, importedServer.username, importedServer.password,
|
importedServer.authenticationType, importedServer.username, password,
|
||||||
importedServer.clientCertificateAlias, extra);
|
importedServer.clientCertificateAlias, extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,16 @@ class AccountActivator(
|
||||||
val account = preferences.getAccount(accountUuid) ?: error("Account $accountUuid not found")
|
val account = preferences.getAccount(accountUuid) ?: error("Account $accountUuid not found")
|
||||||
|
|
||||||
setAccountPasswords(account, incomingServerPassword, outgoingServerPassword)
|
setAccountPasswords(account, incomingServerPassword, outgoingServerPassword)
|
||||||
|
enableAccount(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enableAccount(accountUuid: String) {
|
||||||
|
val account = preferences.getAccount(accountUuid) ?: error("Account $accountUuid not found")
|
||||||
|
|
||||||
|
enableAccount(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun enableAccount(account: Account) {
|
||||||
// Start services if necessary
|
// Start services if necessary
|
||||||
Core.setServicesEnabled(context)
|
Core.setServicesEnabled(context)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.fsck.k9.activity.setup.OAuthFlowActivity
|
||||||
import com.fsck.k9.ui.R
|
import com.fsck.k9.ui.R
|
||||||
import com.fsck.k9.ui.observeNotNull
|
import com.fsck.k9.ui.observeNotNull
|
||||||
import com.mikepenz.fastadapter.FastAdapter
|
import com.mikepenz.fastadapter.FastAdapter
|
||||||
|
@ -103,6 +104,12 @@ class SettingsImportFragment : Fragment() {
|
||||||
StatusText.IMPORT_SUCCESS_PASSWORD_REQUIRED -> {
|
StatusText.IMPORT_SUCCESS_PASSWORD_REQUIRED -> {
|
||||||
statusText.text = getString(R.string.settings_import_password_required)
|
statusText.text = getString(R.string.settings_import_password_required)
|
||||||
}
|
}
|
||||||
|
StatusText.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED -> {
|
||||||
|
statusText.text = getString(R.string.settings_import_authorization_required)
|
||||||
|
}
|
||||||
|
StatusText.IMPORT_SUCCESS_PASSWORD_AND_AUTHORIZATION_REQUIRED -> {
|
||||||
|
statusText.text = getString(R.string.settings_import_authorization_and_password_required)
|
||||||
|
}
|
||||||
StatusText.IMPORT_READ_FAILURE -> {
|
StatusText.IMPORT_READ_FAILURE -> {
|
||||||
statusText.text = getString(R.string.settings_import_read_failure)
|
statusText.text = getString(R.string.settings_import_read_failure)
|
||||||
}
|
}
|
||||||
|
@ -142,6 +149,7 @@ class SettingsImportFragment : Fragment() {
|
||||||
is Action.Close -> closeImportScreen(action)
|
is Action.Close -> closeImportScreen(action)
|
||||||
is Action.PickDocument -> pickDocument()
|
is Action.PickDocument -> pickDocument()
|
||||||
is Action.PasswordPrompt -> showPasswordPrompt(action)
|
is Action.PasswordPrompt -> showPasswordPrompt(action)
|
||||||
|
is Action.StartAuthorization -> startAuthorization(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +168,15 @@ class SettingsImportFragment : Fragment() {
|
||||||
startActivityForResult(createDocumentIntent, REQUEST_PICK_DOCUMENT)
|
startActivityForResult(createDocumentIntent, REQUEST_PICK_DOCUMENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startAuthorization(action: Action.StartAuthorization) {
|
||||||
|
val intent = OAuthFlowActivity.buildLaunchIntent(
|
||||||
|
context = requireContext(),
|
||||||
|
accountUuid = action.accountUuid
|
||||||
|
)
|
||||||
|
|
||||||
|
startActivityForResult(intent, REQUEST_AUTHORIZATION)
|
||||||
|
}
|
||||||
|
|
||||||
private fun showPasswordPrompt(action: Action.PasswordPrompt) {
|
private fun showPasswordPrompt(action: Action.PasswordPrompt) {
|
||||||
val dialogFragment = PasswordPromptDialogFragment.create(
|
val dialogFragment = PasswordPromptDialogFragment.create(
|
||||||
action.accountUuid,
|
action.accountUuid,
|
||||||
|
@ -183,6 +200,7 @@ class SettingsImportFragment : Fragment() {
|
||||||
when (requestCode) {
|
when (requestCode) {
|
||||||
REQUEST_PICK_DOCUMENT -> handlePickDocumentResult(resultCode, data)
|
REQUEST_PICK_DOCUMENT -> handlePickDocumentResult(resultCode, data)
|
||||||
REQUEST_PASSWORD_PROMPT -> handlePasswordPromptResult(resultCode, data)
|
REQUEST_PASSWORD_PROMPT -> handlePasswordPromptResult(resultCode, data)
|
||||||
|
REQUEST_AUTHORIZATION -> handleAuthorizationResult(resultCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,9 +221,16 @@ class SettingsImportFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleAuthorizationResult(resultCode: Int) {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
viewModel.onReturnAfterAuthorization()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val REQUEST_PICK_DOCUMENT = Activity.RESULT_FIRST_USER
|
private const val REQUEST_PICK_DOCUMENT = Activity.RESULT_FIRST_USER
|
||||||
private const val REQUEST_PASSWORD_PROMPT = Activity.RESULT_FIRST_USER + 1
|
private const val REQUEST_PASSWORD_PROMPT = Activity.RESULT_FIRST_USER + 1
|
||||||
|
private const val REQUEST_AUTHORIZATION = Activity.RESULT_FIRST_USER + 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,9 @@ abstract class ImportListItem<VH : ImportCheckBoxViewHolder>(
|
||||||
val imageLevel = when (importStatus) {
|
val imageLevel = when (importStatus) {
|
||||||
ImportStatus.IMPORT_SUCCESS -> 0
|
ImportStatus.IMPORT_SUCCESS -> 0
|
||||||
ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED -> 1
|
ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED -> 1
|
||||||
ImportStatus.NOT_SELECTED -> 2
|
ImportStatus.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED -> 2
|
||||||
ImportStatus.IMPORT_FAILURE -> 3
|
ImportStatus.NOT_SELECTED -> 3
|
||||||
|
ImportStatus.IMPORT_FAILURE -> 4
|
||||||
else -> error("Unexpected import status: $importStatus")
|
else -> error("Unexpected import status: $importStatus")
|
||||||
}
|
}
|
||||||
holder.statusIcon.setImageLevel(imageLevel)
|
holder.statusIcon.setImageLevel(imageLevel)
|
||||||
|
@ -41,6 +42,7 @@ abstract class ImportListItem<VH : ImportCheckBoxViewHolder>(
|
||||||
val contentDescriptionStringResId = when (importStatus) {
|
val contentDescriptionStringResId = when (importStatus) {
|
||||||
ImportStatus.IMPORT_SUCCESS -> R.string.settings_import_status_success
|
ImportStatus.IMPORT_SUCCESS -> R.string.settings_import_status_success
|
||||||
ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED -> R.string.settings_import_status_password_required
|
ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED -> R.string.settings_import_status_password_required
|
||||||
|
ImportStatus.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED -> R.string.settings_import_status_log_in_required
|
||||||
ImportStatus.NOT_SELECTED -> R.string.settings_import_status_not_imported
|
ImportStatus.NOT_SELECTED -> R.string.settings_import_status_not_imported
|
||||||
ImportStatus.IMPORT_FAILURE -> R.string.settings_import_status_error
|
ImportStatus.IMPORT_FAILURE -> R.string.settings_import_status_error
|
||||||
else -> error("Unexpected import status: $importStatus")
|
else -> error("Unexpected import status: $importStatus")
|
||||||
|
|
|
@ -67,13 +67,13 @@ class SettingsImportUiModel {
|
||||||
statusText = StatusText.IMPORT_SUCCESS
|
statusText = StatusText.IMPORT_SUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showPasswordRequiredText() {
|
private fun showActionRequiredText(actionText: StatusText) {
|
||||||
importButton = ButtonState.GONE
|
importButton = ButtonState.GONE
|
||||||
closeButton = ButtonState.ENABLED
|
closeButton = ButtonState.ENABLED
|
||||||
closeButtonLabel = CloseButtonLabel.LATER
|
closeButtonLabel = CloseButtonLabel.LATER
|
||||||
isImportProgressVisible = false
|
isImportProgressVisible = false
|
||||||
isSettingsListEnabled = true
|
isSettingsListEnabled = true
|
||||||
statusText = StatusText.IMPORT_SUCCESS_PASSWORD_REQUIRED
|
statusText = actionText
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showReadFailureText() {
|
fun showReadFailureText() {
|
||||||
|
@ -120,7 +120,7 @@ class SettingsImportUiModel {
|
||||||
|
|
||||||
fun setSettingsListState(position: Int, status: ImportStatus) {
|
fun setSettingsListState(position: Int, status: ImportStatus) {
|
||||||
settingsList[position].importStatus = status
|
settingsList[position].importStatus = status
|
||||||
settingsList[position].enabled = status == ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED
|
settingsList[position].enabled = status.isActionRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateImportButtonFromSelection() {
|
private fun updateImportButtonFromSelection() {
|
||||||
|
@ -141,17 +141,26 @@ class SettingsImportUiModel {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val passwordsMissing = settingsList.any { it.importStatus == ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED }
|
val passwordsMissing = settingsList.any {
|
||||||
if (passwordsMissing) {
|
it.importStatus == ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED
|
||||||
showPasswordRequiredText()
|
}
|
||||||
return
|
val authorizationRequired = settingsList.any {
|
||||||
|
it.importStatus == ImportStatus.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED
|
||||||
}
|
}
|
||||||
|
|
||||||
val partialImportError = settingsList.any { it.importStatus == ImportStatus.IMPORT_FAILURE }
|
if (passwordsMissing && authorizationRequired) {
|
||||||
if (partialImportError) {
|
showActionRequiredText(StatusText.IMPORT_SUCCESS_PASSWORD_AND_AUTHORIZATION_REQUIRED)
|
||||||
showPartialImportErrorText()
|
} else if (passwordsMissing) {
|
||||||
|
showActionRequiredText(StatusText.IMPORT_SUCCESS_PASSWORD_REQUIRED)
|
||||||
|
} else if (authorizationRequired) {
|
||||||
|
showActionRequiredText(StatusText.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED)
|
||||||
} else {
|
} else {
|
||||||
showSuccessText()
|
val partialImportError = settingsList.any { it.importStatus == ImportStatus.IMPORT_FAILURE }
|
||||||
|
if (partialImportError) {
|
||||||
|
showPartialImportErrorText()
|
||||||
|
} else {
|
||||||
|
showSuccessText()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,15 +174,13 @@ sealed class SettingsListItem {
|
||||||
class Account(val accountIndex: Int, var displayName: String) : SettingsListItem()
|
class Account(val accountIndex: Int, var displayName: String) : SettingsListItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ImportStatus {
|
enum class ImportStatus(val isSuccess: Boolean, val isActionRequired: Boolean) {
|
||||||
NOT_AVAILABLE,
|
NOT_AVAILABLE(isSuccess = false, isActionRequired = false),
|
||||||
NOT_SELECTED,
|
NOT_SELECTED(isSuccess = false, isActionRequired = false),
|
||||||
IMPORT_SUCCESS,
|
IMPORT_SUCCESS(isSuccess = true, isActionRequired = false),
|
||||||
IMPORT_SUCCESS_PASSWORD_REQUIRED,
|
IMPORT_SUCCESS_PASSWORD_REQUIRED(isSuccess = true, isActionRequired = true),
|
||||||
IMPORT_FAILURE;
|
IMPORT_SUCCESS_AUTHORIZATION_REQUIRED(isSuccess = true, isActionRequired = true),
|
||||||
|
IMPORT_FAILURE(isSuccess = false, isActionRequired = false)
|
||||||
val isSuccess: Boolean
|
|
||||||
get() = this == IMPORT_SUCCESS || this == IMPORT_SUCCESS_PASSWORD_REQUIRED
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ButtonState {
|
enum class ButtonState {
|
||||||
|
@ -188,6 +195,8 @@ enum class StatusText {
|
||||||
IMPORTING_PROGRESS,
|
IMPORTING_PROGRESS,
|
||||||
IMPORT_SUCCESS,
|
IMPORT_SUCCESS,
|
||||||
IMPORT_SUCCESS_PASSWORD_REQUIRED,
|
IMPORT_SUCCESS_PASSWORD_REQUIRED,
|
||||||
|
IMPORT_SUCCESS_AUTHORIZATION_REQUIRED,
|
||||||
|
IMPORT_SUCCESS_PASSWORD_AND_AUTHORIZATION_REQUIRED,
|
||||||
IMPORT_READ_FAILURE,
|
IMPORT_READ_FAILURE,
|
||||||
IMPORT_PARTIAL_FAILURE,
|
IMPORT_PARTIAL_FAILURE,
|
||||||
IMPORT_FAILURE
|
IMPORT_FAILURE
|
||||||
|
|
|
@ -36,6 +36,7 @@ class SettingsImportViewModel(
|
||||||
private var accountsMap: MutableMap<AccountNumber, AccountUuid> = mutableMapOf()
|
private var accountsMap: MutableMap<AccountNumber, AccountUuid> = mutableMapOf()
|
||||||
private val accountStateMap: MutableMap<AccountNumber, AccountState> = mutableMapOf()
|
private val accountStateMap: MutableMap<AccountNumber, AccountState> = mutableMapOf()
|
||||||
private var contentUri: Uri? = null
|
private var contentUri: Uri? = null
|
||||||
|
private var currentlyAuthorizingAccountUuid: String? = null
|
||||||
|
|
||||||
private val containsGeneralSettings: Boolean
|
private val containsGeneralSettings: Boolean
|
||||||
get() = uiModel.settingsList.any { it is SettingsListItem.GeneralSettings }
|
get() = uiModel.settingsList.any { it is SettingsListItem.GeneralSettings }
|
||||||
|
@ -69,6 +70,7 @@ class SettingsImportViewModel(
|
||||||
|
|
||||||
fun initializeFromSavedState(savedInstanceState: Bundle) {
|
fun initializeFromSavedState(savedInstanceState: Bundle) {
|
||||||
contentUri = savedInstanceState.getParcelable(STATE_CONTENT_URI)
|
contentUri = savedInstanceState.getParcelable(STATE_CONTENT_URI)
|
||||||
|
currentlyAuthorizingAccountUuid = savedInstanceState.getString(STATE_CURRENTLY_AUTHORIZING_ACCOUNT_UUID)
|
||||||
|
|
||||||
updateUiModel {
|
updateUiModel {
|
||||||
isSettingsListVisible = savedInstanceState.getBoolean(STATE_SETTINGS_LIST_VISIBLE)
|
isSettingsListVisible = savedInstanceState.getBoolean(STATE_SETTINGS_LIST_VISIBLE)
|
||||||
|
@ -145,6 +147,7 @@ class SettingsImportViewModel(
|
||||||
outState.putBoolean(STATE_LOADING_PROGRESS_VISIBLE, isLoadingProgressVisible)
|
outState.putBoolean(STATE_LOADING_PROGRESS_VISIBLE, isLoadingProgressVisible)
|
||||||
outState.putBoolean(STATE_IMPORT_PROGRESS_VISIBLE, isImportProgressVisible)
|
outState.putBoolean(STATE_IMPORT_PROGRESS_VISIBLE, isImportProgressVisible)
|
||||||
outState.putEnum(STATE_STATUS_TEXT, statusText)
|
outState.putEnum(STATE_STATUS_TEXT, statusText)
|
||||||
|
outState.putString(STATE_CURRENTLY_AUTHORIZING_ACCOUNT_UUID, currentlyAuthorizingAccountUuid)
|
||||||
|
|
||||||
if (hasDocumentBeenRead) {
|
if (hasDocumentBeenRead) {
|
||||||
val containsGeneralSettings = this@SettingsImportViewModel.containsGeneralSettings
|
val containsGeneralSettings = this@SettingsImportViewModel.containsGeneralSettings
|
||||||
|
@ -200,6 +203,9 @@ class SettingsImportViewModel(
|
||||||
ImportStatus.NOT_AVAILABLE -> updateUiModel {
|
ImportStatus.NOT_AVAILABLE -> updateUiModel {
|
||||||
toggleSettingsListItemSelection(position)
|
toggleSettingsListItemSelection(position)
|
||||||
}
|
}
|
||||||
|
ImportStatus.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED -> {
|
||||||
|
startAuthorization(settingsListItem as SettingsListItem.Account)
|
||||||
|
}
|
||||||
ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED -> {
|
ImportStatus.IMPORT_SUCCESS_PASSWORD_REQUIRED -> {
|
||||||
showPasswordPromptDialog(settingsListItem as SettingsListItem.Account)
|
showPasswordPromptDialog(settingsListItem as SettingsListItem.Account)
|
||||||
}
|
}
|
||||||
|
@ -222,6 +228,21 @@ class SettingsImportViewModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onReturnAfterAuthorization() {
|
||||||
|
currentlyAuthorizingAccountUuid?.let { accountUuid ->
|
||||||
|
currentlyAuthorizingAccountUuid = null
|
||||||
|
updateUiModel {
|
||||||
|
val index = getListIndexOfAccount(accountUuid)
|
||||||
|
setSettingsListState(index, ImportStatus.IMPORT_SUCCESS)
|
||||||
|
updateCloseButtonAndImportStatusText()
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
accountActivator.enableAccount(accountUuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getListIndexOfAccount(accountUuid: String): Int {
|
private fun getListIndexOfAccount(accountUuid: String): Int {
|
||||||
return uiModel.settingsList.indexOfFirst {
|
return uiModel.settingsList.indexOfFirst {
|
||||||
it is SettingsListItem.Account && accountsMap[it.accountIndex] == accountUuid
|
it is SettingsListItem.Account && accountsMap[it.accountIndex] == accountUuid
|
||||||
|
@ -333,7 +354,9 @@ class SettingsImportViewModel(
|
||||||
accountsMap[accountIndex] = accountPair.imported.uuid
|
accountsMap[accountIndex] = accountPair.imported.uuid
|
||||||
listItem.displayName = accountPair.imported.name
|
listItem.displayName = accountPair.imported.name
|
||||||
|
|
||||||
if (accountPair.incomingPasswordNeeded || accountPair.outgoingPasswordNeeded) {
|
if (accountPair.authorizationNeeded) {
|
||||||
|
setSettingsListState(index, ImportStatus.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED)
|
||||||
|
} else if (accountPair.incomingPasswordNeeded || accountPair.outgoingPasswordNeeded) {
|
||||||
accountStateMap[accountIndex] = AccountState(
|
accountStateMap[accountIndex] = AccountState(
|
||||||
accountPair.incomingServerName,
|
accountPair.incomingServerName,
|
||||||
accountPair.outgoingServerName,
|
accountPair.outgoingServerName,
|
||||||
|
@ -364,6 +387,14 @@ class SettingsImportViewModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startAuthorization(settingsListItem: SettingsListItem.Account) {
|
||||||
|
val accountIndex = settingsListItem.accountIndex
|
||||||
|
val accountUuid = accountsMap[accountIndex]!!
|
||||||
|
currentlyAuthorizingAccountUuid = accountUuid
|
||||||
|
|
||||||
|
sendActionEvent(Action.StartAuthorization(accountUuid))
|
||||||
|
}
|
||||||
|
|
||||||
private fun showPasswordPromptDialog(settingsListItem: SettingsListItem.Account) {
|
private fun showPasswordPromptDialog(settingsListItem: SettingsListItem.Account) {
|
||||||
val accountIndex = settingsListItem.accountIndex
|
val accountIndex = settingsListItem.accountIndex
|
||||||
|
|
||||||
|
@ -431,12 +462,14 @@ class SettingsImportViewModel(
|
||||||
private const val STATE_CONTENT_URI = "contentUri"
|
private const val STATE_CONTENT_URI = "contentUri"
|
||||||
private const val STATE_GENERAL_SETTINGS_IMPORT_STATUS = "generalSettingsImportStatus"
|
private const val STATE_GENERAL_SETTINGS_IMPORT_STATUS = "generalSettingsImportStatus"
|
||||||
private const val STATE_ACCOUNT_LIST = "accountList"
|
private const val STATE_ACCOUNT_LIST = "accountList"
|
||||||
|
private const val STATE_CURRENTLY_AUTHORIZING_ACCOUNT_UUID = "currentlyAuthorizingAccountUuid"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Action {
|
sealed class Action {
|
||||||
class Close(val importSuccess: Boolean) : Action()
|
class Close(val importSuccess: Boolean) : Action()
|
||||||
object PickDocument : Action()
|
object PickDocument : Action()
|
||||||
|
class StartAuthorization(val accountUuid: String) : Action()
|
||||||
class PasswordPrompt(
|
class PasswordPrompt(
|
||||||
val accountUuid: String,
|
val accountUuid: String,
|
||||||
val accountName: String,
|
val accountName: String,
|
||||||
|
|
|
@ -9,11 +9,15 @@
|
||||||
android:maxLevel="1"
|
android:maxLevel="1"
|
||||||
android:minLevel="1" />
|
android:minLevel="1" />
|
||||||
<item
|
<item
|
||||||
android:drawable="@drawable/ic_not_imported"
|
android:drawable="@drawable/ic_login"
|
||||||
android:maxLevel="2"
|
android:maxLevel="2"
|
||||||
android:minLevel="2" />
|
android:minLevel="2" />
|
||||||
<item
|
<item
|
||||||
android:drawable="@drawable/ic_error"
|
android:drawable="@drawable/ic_not_imported"
|
||||||
android:maxLevel="3"
|
android:maxLevel="3"
|
||||||
android:minLevel="3" />
|
android:minLevel="3" />
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/ic_error"
|
||||||
|
android:maxLevel="4"
|
||||||
|
android:minLevel="4" />
|
||||||
</level-list>
|
</level-list>
|
||||||
|
|
10
app/ui/legacy/src/main/res/drawable/ic_login.xml
Normal file
10
app/ui/legacy/src/main/res/drawable/ic_login.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M10,17V14H3V10H10V7L15,12L10,17M10,2H19A2,2 0 0,1 21,4V20A2,2 0 0,1 19,22H10A2,2 0 0,1 8,20V18H10V20H19V4H10V6H8V4A2,2 0 0,1 10,2Z" />
|
||||||
|
</vector>
|
|
@ -932,12 +932,23 @@ Please submit bug reports, contribute new features and ask questions at
|
||||||
<string name="settings_import_pick_document_button">Select file</string>
|
<string name="settings_import_pick_document_button">Select file</string>
|
||||||
<string name="settings_import_button">Import</string>
|
<string name="settings_import_button">Import</string>
|
||||||
<string name="settings_import_success_generic">Settings successfully imported</string>
|
<string name="settings_import_success_generic">Settings successfully imported</string>
|
||||||
|
|
||||||
|
<!-- Displayed after importing accounts when one or multiple accounts require entering a password -->
|
||||||
<string name="settings_import_password_required">Please enter passwords</string>
|
<string name="settings_import_password_required">Please enter passwords</string>
|
||||||
|
|
||||||
|
<!-- Displayed after importing accounts when one or multiple accounts require to use the OAuth authorization flow -->
|
||||||
|
<string name="settings_import_authorization_required">Please sign in</string>
|
||||||
|
|
||||||
|
<!-- Displayed after importing accounts when some accounts require entering a password and some to use the OAuth authorization flow -->
|
||||||
|
<string name="settings_import_authorization_and_password_required">Please sign in and enter passwords</string>
|
||||||
|
|
||||||
<string name="settings_import_failure">Failed to import settings</string>
|
<string name="settings_import_failure">Failed to import settings</string>
|
||||||
<string name="settings_import_read_failure">Failed to read settings file</string>
|
<string name="settings_import_read_failure">Failed to read settings file</string>
|
||||||
<string name="settings_import_partial_failure">Failed to import some settings</string>
|
<string name="settings_import_partial_failure">Failed to import some settings</string>
|
||||||
<string name="settings_import_status_success">Imported successfully</string>
|
<string name="settings_import_status_success">Imported successfully</string>
|
||||||
<string name="settings_import_status_password_required">Password required</string>
|
<string name="settings_import_status_password_required">Password required</string>
|
||||||
|
<!-- Content description for the icon that is displayed next to an imported account that requires sign-in -->
|
||||||
|
<string name="settings_import_status_log_in_required">Sign-in required</string>
|
||||||
<string name="settings_import_status_not_imported">Not imported</string>
|
<string name="settings_import_status_not_imported">Not imported</string>
|
||||||
<string name="settings_import_status_error">Import failure</string>
|
<string name="settings_import_status_error">Import failure</string>
|
||||||
<string name="settings_import_later_button">Later</string>
|
<string name="settings_import_later_button">Later</string>
|
||||||
|
|
Loading…
Reference in a new issue