Remove AccountSetupCheckSettings
activity
This commit is contained in:
parent
5f5c17f62e
commit
0f14eb8545
8 changed files with 8 additions and 577 deletions
|
@ -364,6 +364,7 @@ public class MessagingController {
|
|||
put("refreshFolderList", null, () -> refreshFolderListSynchronous(account));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void refreshFolderListSynchronous(Account account) {
|
||||
try {
|
||||
if (isAuthenticationProblem(account, true)) {
|
||||
|
@ -1671,10 +1672,12 @@ public class MessagingController {
|
|||
return getBackend(account).getSupportsFolderSubscriptions();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void checkIncomingServerSettings(Account account) throws MessagingException {
|
||||
getBackend(account).checkIncomingServerSettings();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void checkOutgoingServerSettings(Account account) throws MessagingException {
|
||||
getBackend(account).checkOutgoingServerSettings();
|
||||
}
|
||||
|
@ -2440,6 +2443,7 @@ public class MessagingController {
|
|||
notificationController.removeNewMailNotification(account, messageReference);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void clearCertificateErrorNotifications(Account account, boolean incoming) {
|
||||
notificationController.clearCertificateErrorNotifications(account, incoming);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import androidx.core.app.NotificationCompat
|
|||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.fsck.k9.Account
|
||||
|
||||
@Deprecated("Remove once all usages have been removed for the new account edit feature")
|
||||
internal open class CertificateErrorNotificationController(
|
||||
private val notificationHelper: NotificationHelper,
|
||||
private val actionCreator: NotificationActionCreator,
|
||||
|
|
|
@ -106,13 +106,6 @@
|
|||
android:taskAffinity=""
|
||||
android:theme="@style/Theme.K9.Dialog.Translucent.DayNight"/>
|
||||
|
||||
<!-- XXX Note: this activity is hacked to ignore config changes,
|
||||
since it doesn't currently handle them correctly in code. -->
|
||||
<activity
|
||||
android:name=".activity.setup.AccountSetupCheckSettings"
|
||||
android:configChanges="keyboardHidden|orientation|locale"
|
||||
android:label="@string/account_setup_check_settings_title"/>
|
||||
|
||||
<activity
|
||||
android:name=".ui.endtoend.AutocryptKeyTransferActivity"
|
||||
android:configChanges="locale"
|
||||
|
|
|
@ -1,501 +0,0 @@
|
|||
package com.fsck.k9.activity.setup
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.commit
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.LocalKeyStoreManager
|
||||
import com.fsck.k9.Preferences
|
||||
import com.fsck.k9.controller.MessagingController
|
||||
import com.fsck.k9.fragment.ConfirmationDialogFragment
|
||||
import com.fsck.k9.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.AuthenticationFailedException
|
||||
import com.fsck.k9.mail.CertificateValidationException
|
||||
import com.fsck.k9.mail.MailServerDirection
|
||||
import com.fsck.k9.mail.filter.Hex
|
||||
import com.fsck.k9.ui.R
|
||||
import com.fsck.k9.ui.base.K9Activity
|
||||
import com.fsck.k9.ui.observe
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.security.cert.CertificateEncodingException
|
||||
import java.security.cert.CertificateException
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.Executors
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Checks the given settings to make sure that they can be used to send and receive mail.
|
||||
*
|
||||
* XXX NOTE: The manifest for this app has it ignore config changes, because it doesn't correctly deal with restarting
|
||||
* while its thread is running.
|
||||
*/
|
||||
@Deprecated("Remove once all usages have been removed for the new account edit feature")
|
||||
class AccountSetupCheckSettings : K9Activity(), ConfirmationDialogFragmentListener {
|
||||
private val authViewModel: AuthViewModel by viewModel()
|
||||
|
||||
private val messagingController: MessagingController by inject()
|
||||
private val preferences: Preferences by inject()
|
||||
private val localKeyStoreManager: LocalKeyStoreManager by inject()
|
||||
|
||||
private val handler = Handler(Looper.myLooper()!!)
|
||||
|
||||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var messageView: TextView
|
||||
|
||||
private lateinit var account: Account
|
||||
private lateinit var direction: CheckDirection
|
||||
|
||||
@Volatile
|
||||
private var canceled = false
|
||||
|
||||
@Volatile
|
||||
private var destroyed = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setLayout(R.layout.account_setup_check_settings)
|
||||
|
||||
messageView = findViewById(R.id.message)
|
||||
progressBar = findViewById(R.id.progress)
|
||||
findViewById<View>(R.id.cancel).setOnClickListener { onCancel() }
|
||||
|
||||
setMessage(R.string.account_setup_check_settings_retr_info_msg)
|
||||
progressBar.isIndeterminate = true
|
||||
|
||||
val accountUuid = intent.getStringExtra(EXTRA_ACCOUNT) ?: error("Missing account UUID")
|
||||
account = preferences.getAccount(accountUuid) ?: error("Could not find account")
|
||||
direction = intent.getSerializableExtra(EXTRA_CHECK_DIRECTION) as CheckDirection?
|
||||
?: error("Missing CheckDirection")
|
||||
|
||||
authViewModel.init(activityResultRegistry, lifecycle, account)
|
||||
|
||||
authViewModel.uiState.observe(this) { state ->
|
||||
when (state) {
|
||||
AuthFlowState.Idle -> {
|
||||
return@observe
|
||||
}
|
||||
AuthFlowState.Success -> {
|
||||
startCheckServerSettings()
|
||||
}
|
||||
AuthFlowState.Canceled -> {
|
||||
showErrorDialog(R.string.account_setup_failed_dlg_oauth_flow_canceled)
|
||||
}
|
||||
is AuthFlowState.Failed -> {
|
||||
showErrorDialog(R.string.account_setup_failed_dlg_oauth_flow_failed, state)
|
||||
}
|
||||
AuthFlowState.NotSupported -> {
|
||||
showErrorDialog(R.string.account_setup_failed_dlg_oauth_not_supported)
|
||||
}
|
||||
AuthFlowState.BrowserNotFound -> {
|
||||
showErrorDialog(R.string.account_setup_failed_dlg_browser_not_found)
|
||||
}
|
||||
}
|
||||
|
||||
authViewModel.authResultConsumed()
|
||||
}
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
if (needsAuthorization()) {
|
||||
setMessage(R.string.account_setup_check_settings_authenticate)
|
||||
authViewModel.login()
|
||||
} else {
|
||||
startCheckServerSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun needsAuthorization(): Boolean {
|
||||
return (
|
||||
account.incomingServerSettings.authenticationType == AuthType.XOAUTH2 ||
|
||||
account.outgoingServerSettings.authenticationType == AuthType.XOAUTH2
|
||||
) &&
|
||||
!authViewModel.isAuthorized(account)
|
||||
}
|
||||
|
||||
private fun startCheckServerSettings() {
|
||||
CheckAccountTask(account).executeOnExecutor(Executors.newSingleThreadExecutor(), direction)
|
||||
}
|
||||
|
||||
private fun handleCertificateValidationException(exception: CertificateValidationException) {
|
||||
Timber.e(exception, "Error while testing settings")
|
||||
|
||||
val chain = exception.certChain
|
||||
|
||||
// Avoid NullPointerException in acceptKeyDialog()
|
||||
if (chain != null) {
|
||||
acceptKeyDialog(
|
||||
R.string.account_setup_failed_dlg_certificate_message_fmt,
|
||||
exception,
|
||||
)
|
||||
} else {
|
||||
showErrorDialog(
|
||||
R.string.account_setup_failed_dlg_server_message_fmt,
|
||||
errorMessageForCertificateException(exception)!!,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
destroyed = true
|
||||
canceled = true
|
||||
}
|
||||
|
||||
private fun setMessage(resId: Int) {
|
||||
messageView.text = getString(resId)
|
||||
}
|
||||
|
||||
private fun acceptKeyDialog(msgResId: Int, exception: CertificateValidationException) {
|
||||
handler.post {
|
||||
if (destroyed) {
|
||||
return@post
|
||||
}
|
||||
|
||||
val errorMessage = exception.cause?.cause?.message ?: exception.cause?.message ?: exception.message
|
||||
|
||||
progressBar.isIndeterminate = false
|
||||
|
||||
val chainInfo = StringBuilder()
|
||||
val chain = exception.certChain
|
||||
|
||||
// We already know chain != null (tested before calling this method)
|
||||
for (i in chain.indices) {
|
||||
// display certificate chain information
|
||||
// TODO: localize this strings
|
||||
chainInfo.append("Certificate chain[").append(i).append("]:\n")
|
||||
chainInfo.append("Subject: ").append(chain[i].subjectDN.toString()).append("\n")
|
||||
|
||||
// display SubjectAltNames too
|
||||
// (the user may be mislead into mistrusting a certificate
|
||||
// by a subjectDN not matching the server even though a
|
||||
// SubjectAltName matches)
|
||||
try {
|
||||
val subjectAlternativeNames = chain[i].subjectAlternativeNames
|
||||
if (subjectAlternativeNames != null) {
|
||||
// TODO: localize this string
|
||||
val altNamesText = StringBuilder()
|
||||
altNamesText.append("Subject has ")
|
||||
.append(subjectAlternativeNames.size)
|
||||
.append(" alternative names\n")
|
||||
|
||||
// we need these for matching
|
||||
val incomingServerHost = account.incomingServerSettings.host!!
|
||||
val outgoingServerHost = account.outgoingServerSettings.host!!
|
||||
for (subjectAlternativeName in subjectAlternativeNames) {
|
||||
val type = subjectAlternativeName[0] as Int
|
||||
val value: Any? = subjectAlternativeName[1]
|
||||
val name: String = when (type) {
|
||||
0 -> {
|
||||
Timber.w("SubjectAltName of type OtherName not supported.")
|
||||
continue
|
||||
}
|
||||
1 -> value as String
|
||||
2 -> value as String
|
||||
3 -> {
|
||||
Timber.w("unsupported SubjectAltName of type x400Address")
|
||||
continue
|
||||
}
|
||||
4 -> {
|
||||
Timber.w("unsupported SubjectAltName of type directoryName")
|
||||
continue
|
||||
}
|
||||
5 -> {
|
||||
Timber.w("unsupported SubjectAltName of type ediPartyName")
|
||||
continue
|
||||
}
|
||||
6 -> value as String
|
||||
7 -> value as String
|
||||
else -> {
|
||||
Timber.w("unsupported SubjectAltName of unknown type")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// if some of the SubjectAltNames match the store or transport -host, display them
|
||||
if (name.equals(incomingServerHost, ignoreCase = true) ||
|
||||
name.equals(outgoingServerHost, ignoreCase = true)
|
||||
) {
|
||||
// TODO: localize this string
|
||||
altNamesText.append("Subject(alt): ").append(name).append(",...\n")
|
||||
} else if (name.startsWith("*.") &&
|
||||
(
|
||||
incomingServerHost.endsWith(name.substring(2)) ||
|
||||
outgoingServerHost.endsWith(name.substring(2))
|
||||
)
|
||||
) {
|
||||
// TODO: localize this string
|
||||
altNamesText.append("Subject(alt): ").append(name).append(",...\n")
|
||||
}
|
||||
}
|
||||
chainInfo.append(altNamesText)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// don't fail just because of subjectAltNames
|
||||
Timber.w(e, "cannot display SubjectAltNames in dialog")
|
||||
}
|
||||
|
||||
chainInfo.append("Issuer: ").append(chain[i].issuerDN.toString()).append("\n")
|
||||
for (algorithm in arrayOf("SHA-1", "SHA-256", "SHA-512")) {
|
||||
val digest = try {
|
||||
MessageDigest.getInstance(algorithm)
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
Timber.e(e, "Error while initializing MessageDigest ($algorithm)")
|
||||
null
|
||||
}
|
||||
|
||||
if (digest != null) {
|
||||
digest.reset()
|
||||
try {
|
||||
val hash = Hex.encodeHex(digest.digest(chain[i].encoded))
|
||||
chainInfo.append("Fingerprint ($algorithm): ").append("\n").append(hash).append("\n")
|
||||
} catch (e: CertificateEncodingException) {
|
||||
Timber.e(e, "Error while encoding certificate")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor with DialogFragment.
|
||||
// This is difficult because we need to pass through chain[0] for onClick()
|
||||
AlertDialog.Builder(this@AccountSetupCheckSettings)
|
||||
.setTitle(getString(R.string.account_setup_failed_dlg_invalid_certificate_title))
|
||||
.setMessage(getString(msgResId, errorMessage) + " " + chainInfo.toString())
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.account_setup_failed_dlg_invalid_certificate_accept) { _, _ ->
|
||||
acceptCertificate(chain[0])
|
||||
}
|
||||
.setNegativeButton(R.string.account_setup_failed_dlg_invalid_certificate_reject) { _, _ ->
|
||||
finish()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Permanently accepts a certificate for the INCOMING or OUTGOING direction by adding it to the local key store.
|
||||
*/
|
||||
private fun acceptCertificate(certificate: X509Certificate) {
|
||||
try {
|
||||
localKeyStoreManager.addCertificate(account, direction.toMailServerDirection(), certificate)
|
||||
} catch (e: CertificateException) {
|
||||
showErrorDialog(R.string.account_setup_failed_dlg_certificate_message_fmt, e.message.orEmpty())
|
||||
}
|
||||
|
||||
actionCheckSettings(this@AccountSetupCheckSettings, account, direction)
|
||||
}
|
||||
|
||||
override fun onActivityResult(reqCode: Int, resCode: Int, data: Intent?) {
|
||||
if (reqCode == ACTIVITY_REQUEST_CODE) {
|
||||
setResult(resCode)
|
||||
finish()
|
||||
} else {
|
||||
super.onActivityResult(reqCode, resCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCancel() {
|
||||
canceled = true
|
||||
setMessage(R.string.account_setup_check_settings_canceling_msg)
|
||||
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun showErrorDialog(msgResId: Int, vararg args: Any) {
|
||||
handler.post {
|
||||
showDialogFragment(R.id.dialog_account_setup_error, getString(msgResId, *args))
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDialogFragment(dialogId: Int, customMessage: String) {
|
||||
if (destroyed) return
|
||||
|
||||
progressBar.isIndeterminate = false
|
||||
|
||||
val fragment: DialogFragment = if (dialogId == R.id.dialog_account_setup_error) {
|
||||
ConfirmationDialogFragment.newInstance(
|
||||
dialogId,
|
||||
getString(R.string.account_setup_failed_dlg_title),
|
||||
customMessage,
|
||||
getString(R.string.account_setup_failed_dlg_edit_details_action),
|
||||
getString(R.string.account_setup_failed_dlg_continue_action),
|
||||
)
|
||||
} else {
|
||||
throw RuntimeException("Called showDialog(int) with unknown dialog id.")
|
||||
}
|
||||
|
||||
// TODO: commitAllowingStateLoss() is used to prevent https://code.google.com/p/android/issues/detail?id=23761
|
||||
// but is a bad...
|
||||
supportFragmentManager.commit(allowStateLoss = true) {
|
||||
add(fragment, getDialogTag(dialogId))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDialogTag(dialogId: Int): String {
|
||||
return String.format(Locale.US, "dialog-%d", dialogId)
|
||||
}
|
||||
|
||||
override fun doPositiveClick(dialogId: Int) {
|
||||
if (dialogId == R.id.dialog_account_setup_error) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun doNegativeClick(dialogId: Int) {
|
||||
if (dialogId == R.id.dialog_account_setup_error) {
|
||||
canceled = false
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun dialogCancelled(dialogId: Int) = Unit
|
||||
|
||||
private fun errorMessageForCertificateException(e: CertificateValidationException): String? {
|
||||
return when (e.reason) {
|
||||
CertificateValidationException.Reason.Expired -> {
|
||||
getString(R.string.client_certificate_expired, e.alias, e.message)
|
||||
}
|
||||
CertificateValidationException.Reason.MissingCapability -> {
|
||||
getString(R.string.auth_external_error)
|
||||
}
|
||||
CertificateValidationException.Reason.RetrievalFailure -> {
|
||||
getString(R.string.client_certificate_retrieval_failure, e.alias)
|
||||
}
|
||||
CertificateValidationException.Reason.UseMessage -> {
|
||||
e.message
|
||||
}
|
||||
else -> {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME: Don't use an AsyncTask to perform network operations.
|
||||
* See also discussion in https://github.com/thunderbird/thunderbird-android/pull/560
|
||||
*/
|
||||
private inner class CheckAccountTask(private val account: Account) : AsyncTask<CheckDirection, Int, Unit>() {
|
||||
override fun doInBackground(vararg params: CheckDirection) {
|
||||
val direction = params[0]
|
||||
try {
|
||||
/*
|
||||
* This task could be interrupted at any point, but network operations can block,
|
||||
* so relying on InterruptedException is not enough. Instead, check after
|
||||
* each potentially long-running operation.
|
||||
*/
|
||||
if (isCanceled()) {
|
||||
return
|
||||
}
|
||||
|
||||
clearCertificateErrorNotifications(direction)
|
||||
|
||||
checkServerSettings(direction)
|
||||
|
||||
if (isCanceled()) {
|
||||
return
|
||||
}
|
||||
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
} catch (e: AuthenticationFailedException) {
|
||||
Timber.e(e, "Error while testing settings")
|
||||
showErrorDialog(R.string.account_setup_failed_dlg_auth_message_fmt, e.messageFromServer.orEmpty())
|
||||
} catch (e: CertificateValidationException) {
|
||||
handleCertificateValidationException(e)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error while testing settings")
|
||||
showErrorDialog(R.string.account_setup_failed_dlg_server_message_fmt, e.message.orEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearCertificateErrorNotifications(direction: CheckDirection) {
|
||||
val incoming = direction == CheckDirection.INCOMING
|
||||
messagingController.clearCertificateErrorNotifications(account, incoming)
|
||||
}
|
||||
|
||||
private fun isCanceled(): Boolean {
|
||||
if (destroyed) return true
|
||||
|
||||
if (canceled) {
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun checkServerSettings(direction: CheckDirection) {
|
||||
when (direction) {
|
||||
CheckDirection.INCOMING -> checkIncoming()
|
||||
CheckDirection.OUTGOING -> checkOutgoing()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOutgoing() {
|
||||
publishProgress(R.string.account_setup_check_settings_check_outgoing_msg)
|
||||
|
||||
messagingController.checkOutgoingServerSettings(account)
|
||||
}
|
||||
|
||||
private fun checkIncoming() {
|
||||
publishProgress(R.string.account_setup_check_settings_check_incoming_msg)
|
||||
|
||||
messagingController.checkIncomingServerSettings(account)
|
||||
|
||||
messagingController.refreshFolderListSynchronous(account)
|
||||
|
||||
val inboxFolderId = account.inboxFolderId
|
||||
if (inboxFolderId != null) {
|
||||
messagingController.synchronizeMailbox(account, inboxFolderId, false, null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProgressUpdate(vararg values: Int?) {
|
||||
setMessage(values[0]!!)
|
||||
}
|
||||
}
|
||||
|
||||
enum class CheckDirection {
|
||||
INCOMING, OUTGOING;
|
||||
|
||||
fun toMailServerDirection(): MailServerDirection {
|
||||
return when (this) {
|
||||
INCOMING -> MailServerDirection.INCOMING
|
||||
OUTGOING -> MailServerDirection.OUTGOING
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ACTIVITY_REQUEST_CODE = 1
|
||||
|
||||
private const val EXTRA_ACCOUNT = "account"
|
||||
private const val EXTRA_CHECK_DIRECTION = "checkDirection"
|
||||
|
||||
@JvmStatic
|
||||
fun actionCheckSettings(context: Activity, account: Account, direction: CheckDirection) {
|
||||
val intent = Intent(context, AccountSetupCheckSettings::class.java).apply {
|
||||
putExtra(EXTRA_ACCOUNT, account.uuid)
|
||||
putExtra(EXTRA_CHECK_DIRECTION, direction)
|
||||
}
|
||||
|
||||
context.startActivityForResult(intent, ACTIVITY_REQUEST_CODE)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,9 @@ import android.os.Parcelable
|
|||
import com.fsck.k9.mail.AuthType
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Deprecated(
|
||||
message = "Remove once all usages have been removed for the new account edit feature",
|
||||
)
|
||||
@Parcelize
|
||||
data class InitialAccountSettings(
|
||||
val authenticationType: AuthType,
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context="com.fsck.k9.activity.setup.AccountSetupCheckSettings">
|
||||
|
||||
<include layout="@layout/toolbar" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:padding="6dip"
|
||||
android:fadingEdge="none"
|
||||
android:scrollbarStyle="outsideInset">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center_horizontal|center_vertical"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="6dip"
|
||||
android:layout_width="fill_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:gravity="center_horizontal"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:paddingBottom="6dip"
|
||||
android:focusable="true"
|
||||
/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="center_horizontal|center_vertical"
|
||||
android:focusable="true"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
<include layout="@layout/wizard_cancel"/>
|
||||
</LinearLayout>
|
|
@ -6,7 +6,6 @@
|
|||
<item type="id" name="dialog_confirm_delete"/>
|
||||
<item type="id" name="dialog_confirm_spam"/>
|
||||
<item type="id" name="dialog_attachment_progress"/>
|
||||
<item type="id" name="dialog_account_setup_error"/>
|
||||
|
||||
<item type="id" name="settings_export_list_general_item"/>
|
||||
<item type="id" name="settings_export_list_account_item"/>
|
||||
|
|
|
@ -356,13 +356,7 @@
|
|||
<string name="account_setup_basics_show_password_biometrics_title">Verify your identity</string>
|
||||
<string name="account_setup_basics_show_password_biometrics_subtitle">Unlock to view your password</string>
|
||||
|
||||
<string name="account_setup_check_settings_title" />
|
||||
<string name="account_setup_check_settings_retr_info_msg">Retrieving account information\u2026</string>
|
||||
<string name="account_setup_check_settings_check_incoming_msg">Checking incoming server settings\u2026</string>
|
||||
<string name="account_setup_check_settings_check_outgoing_msg">Checking outgoing server settings\u2026</string>
|
||||
<string name="account_setup_check_settings_authenticate">Authenticating\u2026</string>
|
||||
<string name="account_setup_check_settings_fetch">Fetching account settings\u2026</string>
|
||||
<string name="account_setup_check_settings_canceling_msg">Canceling\u2026</string>
|
||||
|
||||
<string name="account_setup_auth_type_normal_password">Normal password</string>
|
||||
<string name="account_setup_auth_type_insecure_password">Password, transmitted insecurely</string>
|
||||
|
@ -430,16 +424,10 @@
|
|||
|
||||
<string name="move_copy_cannot_copy_unsynced_message">Cannot copy or move a message that is not synchronized with the server</string>
|
||||
|
||||
<string name="account_setup_failed_dlg_title">Setup could not finish</string>
|
||||
<string name="account_setup_failed_dlg_auth_message_fmt">Username or password incorrect.\n(<xliff:g id="error">%s</xliff:g>)</string>
|
||||
<string name="account_setup_failed_dlg_certificate_message_fmt">The server presented an invalid SSL certificate. Sometimes, this is because of a server misconfiguration. Sometimes it is because someone is trying to attack you or your mail server. If you\'re not sure what\'s up, click Reject and contact the folks who manage your mail server.\n\n(<xliff:g id="error">%s</xliff:g>)</string>
|
||||
<string name="account_setup_failed_dlg_server_message_fmt">Cannot connect to server.\n(<xliff:g id="error">%s</xliff:g>)</string>
|
||||
<string name="account_setup_failed_dlg_oauth_flow_canceled">Authorization canceled</string>
|
||||
<string name="account_setup_failed_dlg_oauth_flow_failed">Authorization failed with the following error: <xliff:g id="error">%s</xliff:g></string>
|
||||
<string name="account_setup_failed_dlg_oauth_not_supported">OAuth 2.0 is currently not supported with this provider.</string>
|
||||
<string name="account_setup_failed_dlg_browser_not_found">The app couldn\'t find a browser to use for granting access to your account.</string>
|
||||
<string name="account_setup_failed_dlg_edit_details_action">Edit details</string>
|
||||
<string name="account_setup_failed_dlg_continue_action">Continue</string>
|
||||
|
||||
<string name="account_settings_push_advanced_title">Advanced</string>
|
||||
<string name="account_settings_title_fmt">Account settings</string>
|
||||
|
@ -716,10 +704,6 @@
|
|||
|
||||
<string name="account_delete_dlg_title">Remove Account</string>
|
||||
|
||||
<string name="account_setup_failed_dlg_invalid_certificate_title">Unrecognized Certificate</string>
|
||||
<string name="account_setup_failed_dlg_invalid_certificate_accept">Accept Key</string>
|
||||
<string name="account_setup_failed_dlg_invalid_certificate_reject">Reject Key</string>
|
||||
|
||||
<string name="folder_list_filter_hint">Folder name contains</string>
|
||||
|
||||
<string name="folder_list_display_mode_label">Show folders…</string>
|
||||
|
@ -1011,13 +995,9 @@
|
|||
<string name="fetching_attachment_dialog_title_save">Saving draft</string>
|
||||
<string name="fetching_attachment_dialog_message">Fetching attachment…</string>
|
||||
|
||||
<string name="auth_external_error">Unable to authenticate. The server does not advertise the SASL EXTERNAL capability. This could be due to a problem with the client certificate (expired, unknown certificate authority) or some other configuration problem.</string>
|
||||
|
||||
<!-- === Client certificates specific ================================================================== -->
|
||||
<string name="client_certificate_spinner_empty">No client certificate</string>
|
||||
<string name="client_certificate_spinner_delete">Remove client certificate selection</string>
|
||||
<string name="client_certificate_retrieval_failure">"Failed to retrieve client certificate for alias \"<xliff:g id="alias">%s</xliff:g>\""</string>
|
||||
<string name="client_certificate_expired">"Client certificate \"<xliff:g id="certificate_alias">%1$s</xliff:g>\" has expired or is not yet valid (<xliff:g id="exception_message">%2$s</xliff:g>)"</string>
|
||||
|
||||
<!-- Note: This references message_view_download_remainder -->
|
||||
<string name="preview_encrypted">*Encrypted*</string>
|
||||
|
|
Loading…
Reference in a new issue