Convert AccountSetupCheckSettings
to Kotlin
This commit is contained in:
parent
af2d031385
commit
f71615f6aa
2 changed files with 364 additions and 428 deletions
|
@ -256,14 +256,14 @@ class AccountSetupBasics : K9Activity() {
|
|||
}
|
||||
|
||||
if (resultCode == RESULT_OK) {
|
||||
val account = this.account ?: error("Account instance missing")
|
||||
|
||||
if (!checkedIncoming) {
|
||||
// We've successfully checked incoming. Now check outgoing.
|
||||
checkedIncoming = true
|
||||
AccountSetupCheckSettings.actionCheckSettings(this, account, CheckDirection.OUTGOING)
|
||||
} else {
|
||||
// We've successfully checked outgoing as well.
|
||||
val account = this.account ?: error("Account instance missing")
|
||||
|
||||
preferences.saveAccount(account)
|
||||
Core.setServicesEnabled(applicationContext)
|
||||
|
||||
|
|
|
@ -1,408 +1,336 @@
|
|||
package com.fsck.k9.activity.setup
|
||||
|
||||
package com.fsck.k9.activity.setup;
|
||||
|
||||
|
||||
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.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.DI;
|
||||
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.AuthenticationFailedException;
|
||||
import com.fsck.k9.mail.CertificateValidationException;
|
||||
import com.fsck.k9.mail.MailServerDirection;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.filter.Hex;
|
||||
import com.fsck.k9.preferences.Protocols;
|
||||
import com.fsck.k9.ui.R;
|
||||
import com.fsck.k9.ui.base.K9Activity;
|
||||
import timber.log.Timber;
|
||||
|
||||
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.AuthenticationFailedException
|
||||
import com.fsck.k9.mail.CertificateValidationException
|
||||
import com.fsck.k9.mail.MailServerDirection
|
||||
import com.fsck.k9.mail.filter.Hex
|
||||
import com.fsck.k9.preferences.Protocols
|
||||
import com.fsck.k9.ui.R
|
||||
import com.fsck.k9.ui.base.K9Activity
|
||||
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 org.koin.android.ext.android.inject
|
||||
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.
|
||||
* 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.
|
||||
*/
|
||||
public class AccountSetupCheckSettings extends K9Activity implements OnClickListener,
|
||||
ConfirmationDialogFragmentListener{
|
||||
class AccountSetupCheckSettings : K9Activity(), ConfirmationDialogFragmentListener {
|
||||
private val messagingController: MessagingController by inject()
|
||||
private val preferences: Preferences by inject()
|
||||
private val localKeyStoreManager: LocalKeyStoreManager by inject()
|
||||
|
||||
public static final int ACTIVITY_REQUEST_CODE = 1;
|
||||
private val handler = Handler(Looper.myLooper()!!)
|
||||
|
||||
private static final String EXTRA_ACCOUNT = "account";
|
||||
private lateinit var progressBar: ProgressBar
|
||||
private lateinit var messageView: TextView
|
||||
|
||||
private static final String EXTRA_CHECK_DIRECTION ="checkDirection";
|
||||
private lateinit var account: Account
|
||||
private lateinit var direction: CheckDirection
|
||||
|
||||
public enum CheckDirection {
|
||||
INCOMING,
|
||||
OUTGOING;
|
||||
@Volatile
|
||||
private var canceled = false
|
||||
|
||||
public MailServerDirection toMailServerDirection() {
|
||||
switch (this) {
|
||||
case INCOMING: return MailServerDirection.INCOMING;
|
||||
case OUTGOING: return MailServerDirection.OUTGOING;
|
||||
}
|
||||
@Volatile
|
||||
private var destroyed = false
|
||||
|
||||
throw new AssertionError("Unknown value: " + this);
|
||||
}
|
||||
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")
|
||||
|
||||
CheckAccountTask(account).execute(direction)
|
||||
}
|
||||
|
||||
private final MessagingController messagingController = DI.get(MessagingController.class);
|
||||
private fun handleCertificateValidationException(exception: CertificateValidationException) {
|
||||
Timber.e(exception, "Error while testing settings")
|
||||
|
||||
private Handler mHandler = new Handler();
|
||||
val chain = exception.certChain
|
||||
|
||||
private ProgressBar mProgressBar;
|
||||
|
||||
private TextView mMessageView;
|
||||
|
||||
private Account mAccount;
|
||||
|
||||
private CheckDirection mDirection;
|
||||
|
||||
private boolean mCanceled;
|
||||
|
||||
private boolean mDestroyed;
|
||||
|
||||
public static void actionCheckSettings(Activity context, Account account,
|
||||
CheckDirection direction) {
|
||||
Intent i = new Intent(context, AccountSetupCheckSettings.class);
|
||||
i.putExtra(EXTRA_ACCOUNT, account.getUuid());
|
||||
i.putExtra(EXTRA_CHECK_DIRECTION, direction);
|
||||
context.startActivityForResult(i, ACTIVITY_REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setLayout(R.layout.account_setup_check_settings);
|
||||
mMessageView = findViewById(R.id.message);
|
||||
mProgressBar = findViewById(R.id.progress);
|
||||
findViewById(R.id.cancel).setOnClickListener(this);
|
||||
|
||||
setMessage(R.string.account_setup_check_settings_retr_info_msg);
|
||||
mProgressBar.setIndeterminate(true);
|
||||
|
||||
String accountUuid = getIntent().getStringExtra(EXTRA_ACCOUNT);
|
||||
mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
|
||||
mDirection = (CheckDirection) getIntent().getSerializableExtra(EXTRA_CHECK_DIRECTION);
|
||||
|
||||
new CheckAccountTask(mAccount).execute(mDirection);
|
||||
}
|
||||
|
||||
private void handleCertificateValidationException(CertificateValidationException cve) {
|
||||
Timber.e(cve, "Error while testing settings");
|
||||
|
||||
X509Certificate[] chain = cve.getCertChain();
|
||||
// Avoid NullPointerException in acceptKeyDialog()
|
||||
if (chain != null) {
|
||||
acceptKeyDialog(
|
||||
R.string.account_setup_failed_dlg_certificate_message_fmt,
|
||||
cve);
|
||||
R.string.account_setup_failed_dlg_certificate_message_fmt,
|
||||
exception
|
||||
)
|
||||
} else {
|
||||
showErrorDialog(
|
||||
R.string.account_setup_failed_dlg_server_message_fmt,
|
||||
errorMessageForCertificateException(cve));
|
||||
R.string.account_setup_failed_dlg_server_message_fmt,
|
||||
errorMessageForCertificateException(exception)!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mDestroyed = true;
|
||||
mCanceled = true;
|
||||
destroyed = true
|
||||
canceled = true
|
||||
}
|
||||
|
||||
private void setMessage(final int resId) {
|
||||
mMessageView.setText(getString(resId));
|
||||
private fun setMessage(resId: Int) {
|
||||
messageView.text = getString(resId)
|
||||
}
|
||||
|
||||
private void acceptKeyDialog(final int msgResId, final CertificateValidationException ex) {
|
||||
mHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
String exMessage = "Unknown Error";
|
||||
|
||||
if (ex != null) {
|
||||
if (ex.getCause() != null) {
|
||||
if (ex.getCause().getCause() != null) {
|
||||
exMessage = ex.getCause().getCause().getMessage();
|
||||
|
||||
} else {
|
||||
exMessage = ex.getCause().getMessage();
|
||||
}
|
||||
} else {
|
||||
exMessage = ex.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
mProgressBar.setIndeterminate(false);
|
||||
StringBuilder chainInfo = new StringBuilder(200);
|
||||
final X509Certificate[] chain = ex.getCertChain();
|
||||
// We already know chain != null (tested before calling this method)
|
||||
for (int i = 0; i < chain.length; i++) {
|
||||
// display certificate chain information
|
||||
//TODO: localize this strings
|
||||
chainInfo.append("Certificate chain[").append(i).append("]:\n");
|
||||
chainInfo.append("Subject: ").append(chain[i].getSubjectDN().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 {
|
||||
final Collection < List<? >> subjectAlternativeNames = chain[i].getSubjectAlternativeNames();
|
||||
if (subjectAlternativeNames != null) {
|
||||
// The list of SubjectAltNames may be very long
|
||||
//TODO: localize this string
|
||||
StringBuilder altNamesText = new StringBuilder();
|
||||
altNamesText.append("Subject has ").append(subjectAlternativeNames.size()).append(" alternative names\n");
|
||||
|
||||
// we need these for matching
|
||||
String incomingServerHost = mAccount.getIncomingServerSettings().host;
|
||||
String outgoingServerHost = mAccount.getOutgoingServerSettings().host;
|
||||
|
||||
for (List<?> subjectAlternativeName : subjectAlternativeNames) {
|
||||
Integer type = (Integer)subjectAlternativeName.get(0);
|
||||
Object value = subjectAlternativeName.get(1);
|
||||
String name;
|
||||
switch (type) {
|
||||
case 0:
|
||||
Timber.w("SubjectAltName of type OtherName not supported.");
|
||||
continue;
|
||||
case 1: // RFC822Name
|
||||
name = (String)value;
|
||||
break;
|
||||
case 2: // DNSName
|
||||
name = (String)value;
|
||||
break;
|
||||
case 3:
|
||||
Timber.w("unsupported SubjectAltName of type x400Address");
|
||||
continue;
|
||||
case 4:
|
||||
Timber.w("unsupported SubjectAltName of type directoryName");
|
||||
continue;
|
||||
case 5:
|
||||
Timber.w("unsupported SubjectAltName of type ediPartyName");
|
||||
continue;
|
||||
case 6: // Uri
|
||||
name = (String)value;
|
||||
break;
|
||||
case 7: // ip-address
|
||||
name = (String)value;
|
||||
break;
|
||||
default:
|
||||
Timber.w("unsupported SubjectAltName of unknown type");
|
||||
continue;
|
||||
}
|
||||
|
||||
// if some of the SubjectAltNames match the store or transport -host,
|
||||
// display them
|
||||
if (name.equalsIgnoreCase(incomingServerHost) || name.equalsIgnoreCase(outgoingServerHost)) {
|
||||
//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 (Exception e1) {
|
||||
// don't fail just because of subjectAltNames
|
||||
Timber.w(e1, "cannot display SubjectAltNames in dialog");
|
||||
}
|
||||
|
||||
chainInfo.append("Issuer: ").append(chain[i].getIssuerDN().toString()).append("\n");
|
||||
String[] digestAlgorithms = new String[] {"SHA-1", "SHA-256", "SHA-512"};
|
||||
|
||||
for (String algorithm : digestAlgorithms) {
|
||||
MessageDigest digest = null;
|
||||
try {
|
||||
digest = MessageDigest.getInstance(algorithm);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Timber.e(e, "Error while initializing MessageDigest (" + algorithm + ")");
|
||||
}
|
||||
|
||||
if (digest != null) {
|
||||
digest.reset();
|
||||
try {
|
||||
String hash = Hex.encodeHex(digest.digest(chain[i].getEncoded()));
|
||||
chainInfo.append("Fingerprint ("+ algorithm +"): ").append("\n").append(hash).append("\n");
|
||||
} catch (CertificateEncodingException e) {
|
||||
Timber.e(e, "Error while encoding certificate");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor with DialogFragment.
|
||||
// This is difficult because we need to pass through chain[0] for onClick()
|
||||
new AlertDialog.Builder(AccountSetupCheckSettings.this)
|
||||
.setTitle(getString(R.string.account_setup_failed_dlg_invalid_certificate_title))
|
||||
//.setMessage(getString(R.string.account_setup_failed_dlg_invalid_certificate)
|
||||
.setMessage(getString(msgResId, exMessage)
|
||||
+ " " + chainInfo.toString()
|
||||
)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(
|
||||
getString(R.string.account_setup_failed_dlg_invalid_certificate_accept),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
acceptCertificate(chain[0]);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(
|
||||
getString(R.string.account_setup_failed_dlg_invalid_certificate_reject),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
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.
|
||||
*
|
||||
* @param certificate
|
||||
* Permanently accepts a certificate for the INCOMING or OUTGOING direction by adding it to the local key store.
|
||||
*/
|
||||
private void acceptCertificate(X509Certificate certificate) {
|
||||
private fun acceptCertificate(certificate: X509Certificate) {
|
||||
try {
|
||||
DI.get(LocalKeyStoreManager.class).addCertificate(mAccount, mDirection.toMailServerDirection(), certificate);
|
||||
} catch (CertificateException e) {
|
||||
showErrorDialog(
|
||||
R.string.account_setup_failed_dlg_certificate_message_fmt,
|
||||
e.getMessage() == null ? "" : e.getMessage());
|
||||
localKeyStoreManager.addCertificate(account, direction.toMailServerDirection(), certificate)
|
||||
} catch (e: CertificateException) {
|
||||
showErrorDialog(R.string.account_setup_failed_dlg_certificate_message_fmt, e.message.orEmpty())
|
||||
}
|
||||
AccountSetupCheckSettings.actionCheckSettings(AccountSetupCheckSettings.this, mAccount,
|
||||
mDirection);
|
||||
|
||||
actionCheckSettings(this@AccountSetupCheckSettings, account, direction)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int reqCode, int resCode, Intent data) {
|
||||
override fun onActivityResult(reqCode: Int, resCode: Int, data: Intent?) {
|
||||
if (reqCode == ACTIVITY_REQUEST_CODE) {
|
||||
setResult(resCode);
|
||||
finish();
|
||||
setResult(resCode)
|
||||
finish()
|
||||
} else {
|
||||
super.onActivityResult(reqCode, resCode, data);
|
||||
super.onActivityResult(reqCode, resCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
private void onCancel() {
|
||||
mCanceled = true;
|
||||
setMessage(R.string.account_setup_check_settings_canceling_msg);
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
private fun onCancel() {
|
||||
canceled = true
|
||||
setMessage(R.string.account_setup_check_settings_canceling_msg)
|
||||
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.cancel) {
|
||||
onCancel();
|
||||
private fun showErrorDialog(msgResId: Int, vararg args: Any) {
|
||||
handler.post {
|
||||
showDialogFragment(R.id.dialog_account_setup_error, getString(msgResId, *args))
|
||||
}
|
||||
}
|
||||
|
||||
private void showErrorDialog(final int msgResId, final Object... args) {
|
||||
mHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
showDialogFragment(R.id.dialog_account_setup_error, getString(msgResId, args));
|
||||
}
|
||||
});
|
||||
}
|
||||
private fun showDialogFragment(dialogId: Int, customMessage: String) {
|
||||
if (destroyed) return
|
||||
|
||||
private void showDialogFragment(int dialogId, String customMessage) {
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
mProgressBar.setIndeterminate(false);
|
||||
progressBar.isIndeterminate = false
|
||||
|
||||
DialogFragment fragment;
|
||||
if (dialogId == R.id.dialog_account_setup_error) {
|
||||
fragment = 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)
|
||||
);
|
||||
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 new RuntimeException("Called showDialog(int) with unknown dialog id.");
|
||||
throw RuntimeException("Called showDialog(int) with unknown dialog id.")
|
||||
}
|
||||
|
||||
FragmentTransaction ta = getSupportFragmentManager().beginTransaction();
|
||||
ta.add(fragment, getDialogTag(dialogId));
|
||||
ta.commitAllowingStateLoss();
|
||||
|
||||
// TODO: commitAllowingStateLoss() is used to prevent https://code.google.com/p/android/issues/detail?id=23761
|
||||
// but is a bad...
|
||||
//fragment.show(ta, getDialogTag(dialogId));
|
||||
}
|
||||
|
||||
private String getDialogTag(int dialogId) {
|
||||
return String.format(Locale.US, "dialog-%d", dialogId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPositiveClick(int dialogId) {
|
||||
if (dialogId == R.id.dialog_account_setup_error) {
|
||||
finish();
|
||||
supportFragmentManager.commit(allowStateLoss = true) {
|
||||
add(fragment, getDialogTag(dialogId))
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doNegativeClick(int 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) {
|
||||
mCanceled = false;
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dialogCancelled(int dialogId) {
|
||||
// nothing to do here...
|
||||
override fun doNegativeClick(dialogId: Int) {
|
||||
if (dialogId == R.id.dialog_account_setup_error) {
|
||||
canceled = false
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
private String errorMessageForCertificateException(CertificateValidationException e) {
|
||||
switch (e.getReason()) {
|
||||
case Expired: return getString(R.string.client_certificate_expired, e.getAlias(), e.getMessage());
|
||||
case MissingCapability: return getString(R.string.auth_external_error);
|
||||
case RetrievalFailure: return getString(R.string.client_certificate_retrieval_failure, e.getAlias());
|
||||
case UseMessage: return e.getMessage();
|
||||
case Unknown:
|
||||
default: return "";
|
||||
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 -> {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -410,117 +338,125 @@ public class AccountSetupCheckSettings extends K9Activity implements OnClickList
|
|||
* FIXME: Don't use an AsyncTask to perform network operations.
|
||||
* See also discussion in https://github.com/k9mail/k-9/pull/560
|
||||
*/
|
||||
private class CheckAccountTask extends AsyncTask<CheckDirection, Integer, Void> {
|
||||
private final Account account;
|
||||
|
||||
private CheckAccountTask(Account account) {
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(CheckDirection... params) {
|
||||
final CheckDirection direction = params[0];
|
||||
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 (cancelled()) {
|
||||
return null;
|
||||
if (isCanceled()) {
|
||||
return
|
||||
}
|
||||
|
||||
clearCertificateErrorNotifications(direction);
|
||||
clearCertificateErrorNotifications(direction)
|
||||
|
||||
checkServerSettings(direction);
|
||||
checkServerSettings(direction)
|
||||
|
||||
if (cancelled()) {
|
||||
return null;
|
||||
if (isCanceled()) {
|
||||
return
|
||||
}
|
||||
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
|
||||
} catch (AuthenticationFailedException afe) {
|
||||
Timber.e(afe, "Error while testing settings");
|
||||
showErrorDialog(
|
||||
R.string.account_setup_failed_dlg_auth_message_fmt,
|
||||
afe.getMessage() == null ? "" : afe.getMessage());
|
||||
} catch (CertificateValidationException cve) {
|
||||
handleCertificateValidationException(cve);
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Error while testing settings");
|
||||
String message = e.getMessage() == null ? "" : e.getMessage();
|
||||
showErrorDialog(R.string.account_setup_failed_dlg_server_message_fmt, message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void clearCertificateErrorNotifications(CheckDirection direction) {
|
||||
final MessagingController ctrl = MessagingController.getInstance(getApplication());
|
||||
boolean incoming = (direction == CheckDirection.INCOMING);
|
||||
ctrl.clearCertificateErrorNotifications(account, incoming);
|
||||
}
|
||||
|
||||
private boolean cancelled() {
|
||||
if (mDestroyed) {
|
||||
return true;
|
||||
}
|
||||
if (mCanceled) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkServerSettings(CheckDirection direction) throws MessagingException {
|
||||
switch (direction) {
|
||||
case INCOMING: {
|
||||
checkIncoming();
|
||||
break;
|
||||
}
|
||||
case OUTGOING: {
|
||||
checkOutgoing();
|
||||
break;
|
||||
}
|
||||
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.message.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 void checkOutgoing() throws MessagingException {
|
||||
if (!isWebDavAccount()) {
|
||||
publishProgress(R.string.account_setup_check_settings_check_outgoing_msg);
|
||||
}
|
||||
|
||||
messagingController.checkOutgoingServerSettings(account);
|
||||
private fun clearCertificateErrorNotifications(direction: CheckDirection) {
|
||||
val incoming = direction == CheckDirection.INCOMING
|
||||
messagingController.clearCertificateErrorNotifications(account, incoming)
|
||||
}
|
||||
|
||||
private void checkIncoming() throws MessagingException {
|
||||
if (isWebDavAccount()) {
|
||||
publishProgress(R.string.account_setup_check_settings_authenticate);
|
||||
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() {
|
||||
if (!isWebDavAccount) {
|
||||
publishProgress(R.string.account_setup_check_settings_check_outgoing_msg)
|
||||
}
|
||||
|
||||
messagingController.checkOutgoingServerSettings(account)
|
||||
}
|
||||
|
||||
private fun checkIncoming() {
|
||||
if (isWebDavAccount) {
|
||||
publishProgress(R.string.account_setup_check_settings_authenticate)
|
||||
} else {
|
||||
publishProgress(R.string.account_setup_check_settings_check_incoming_msg);
|
||||
publishProgress(R.string.account_setup_check_settings_check_incoming_msg)
|
||||
}
|
||||
|
||||
messagingController.checkIncomingServerSettings(account);
|
||||
messagingController.checkIncomingServerSettings(account)
|
||||
|
||||
if (isWebDavAccount()) {
|
||||
publishProgress(R.string.account_setup_check_settings_fetch);
|
||||
if (isWebDavAccount) {
|
||||
publishProgress(R.string.account_setup_check_settings_fetch)
|
||||
}
|
||||
|
||||
messagingController.refreshFolderListSynchronous(account);
|
||||
Long inboxFolderId = account.getInboxFolderId();
|
||||
messagingController.refreshFolderListSynchronous(account)
|
||||
|
||||
val inboxFolderId = account.inboxFolderId
|
||||
if (inboxFolderId != null) {
|
||||
messagingController.synchronizeMailbox(account, inboxFolderId, false, null);
|
||||
messagingController.synchronizeMailbox(account, inboxFolderId, false, null)
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isWebDavAccount() {
|
||||
return account.getIncomingServerSettings().type.equals(Protocols.WEBDAV);
|
||||
}
|
||||
private val isWebDavAccount: Boolean
|
||||
get() = account.incomingServerSettings.type == Protocols.WEBDAV
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values) {
|
||||
setMessage(values[0]);
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue