diff --git a/res/layout/accounts_password_prompt.xml b/res/layout/accounts_password_prompt.xml new file mode 100644 index 000000000..59b33ecbd --- /dev/null +++ b/res/layout/accounts_password_prompt.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + diff --git a/res/menu/disabled_accounts_context.xml b/res/menu/disabled_accounts_context.xml new file mode 100644 index 000000000..54d6c2afc --- /dev/null +++ b/res/menu/disabled_accounts_context.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index c68dc99bd..b5484097c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1093,6 +1093,19 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin Export failed Import succeeded Import failed + Activate account + To be able to use the account \"%s\" you need to provide the %s. + + server password + server passwords + + Incoming server (%s): + Outgoing server (%s): + + Setting password... + Setting passwords... + + Activate Unable to handle file of version %s diff --git a/src/com/fsck/k9/Account.java b/src/com/fsck/k9/Account.java index 50f9d8459..34779d19f 100644 --- a/src/com/fsck/k9/Account.java +++ b/src/com/fsck/k9/Account.java @@ -148,6 +148,16 @@ public class Account implements BaseAccount { private CryptoProvider mCryptoProvider = null; + /** + * Indicates whether this account is enabled, i.e. ready for use, or not. + * + *

+ * Right now newly imported accounts are disabled if the settings file didn't contain a + * password for the incoming and/or outgoing server. + *

+ */ + private boolean mEnabled; + /** * Name of the folder that was last selected for a copy or move operation. * @@ -224,6 +234,7 @@ public class Account implements BaseAccount { mSyncRemoteDeletions = true; mCryptoApp = Apg.NAME; mCryptoAutoSignature = false; + mEnabled = true; searchableFolders = Searchable.ALL; @@ -386,6 +397,7 @@ public class Account implements BaseAccount { mCryptoApp = prefs.getString(mUuid + ".cryptoApp", Apg.NAME); mCryptoAutoSignature = prefs.getBoolean(mUuid + ".cryptoAutoSignature", false); + mEnabled = prefs.getBoolean(mUuid + ".enabled", true); } private String combineUuids(String[] uuids) { @@ -467,6 +479,7 @@ public class Account implements BaseAccount { editor.remove(mUuid + ".replyAfterQuote"); editor.remove(mUuid + ".cryptoApp"); editor.remove(mUuid + ".cryptoAutoSignature"); + editor.remove(mUuid + ".enabled"); editor.remove(mUuid + ".enableMoveButtons"); editor.remove(mUuid + ".hideMoveButtonsEnum"); for (String type : networkTypes) { @@ -619,6 +632,7 @@ public class Account implements BaseAccount { editor.putBoolean(mUuid + ".replyAfterQuote", mReplyAfterQuote); editor.putString(mUuid + ".cryptoApp", mCryptoApp); editor.putBoolean(mUuid + ".cryptoAutoSignature", mCryptoAutoSignature); + editor.putBoolean(mUuid + ".enabled", mEnabled); editor.putBoolean(mUuid + ".vibrate", mNotificationSetting.shouldVibrate()); editor.putInt(mUuid + ".vibratePattern", mNotificationSetting.getVibratePattern()); @@ -1480,4 +1494,11 @@ public class Account implements BaseAccount { return StorageManager.getInstance(K9.app).isReady(localStorageProviderId); } + public synchronized boolean isEnabled() { + return mEnabled; + } + + public synchronized void setEnabled(boolean enabled) { + mEnabled = enabled; + } } diff --git a/src/com/fsck/k9/Preferences.java b/src/com/fsck/k9/Preferences.java index 460dcb4ae..2def52c9b 100644 --- a/src/com/fsck/k9/Preferences.java +++ b/src/com/fsck/k9/Preferences.java @@ -98,6 +98,23 @@ public class Preferences { return retval; } + /** + * Returns all enabled accounts. + * + * @return All accounts with {@link Account#isEnabled()} + */ + public List getEnabledAccounts() { + Account[] allAccounts = getAccounts(); + List enabledAccounts = new ArrayList(); + for (Account account : allAccounts) { + if (account.isEnabled()) { + enabledAccounts.add(account); + } + } + + return enabledAccounts; + } + public synchronized Account getAccount(String uuid) { if (accounts == null) { loadAccounts(); @@ -116,12 +133,12 @@ public class Preferences { } public synchronized void deleteAccount(Account account) { - if (accounts != null) { - accounts.remove(account.getUuid()); - } - if (accountsInOrder != null) { - accountsInOrder.remove(account); - } + if (accounts != null) { + accounts.remove(account.getUuid()); + } + if (accountsInOrder != null) { + accountsInOrder.remove(account); + } account.delete(this); diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java index 0f951e7d8..98b3af58f 100644 --- a/src/com/fsck/k9/activity/Accounts.java +++ b/src/com/fsck/k9/activity/Accounts.java @@ -26,6 +26,8 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.text.Editable; +import android.text.TextWatcher; import android.util.Log; import android.util.SparseBooleanArray; import android.util.TypedValue; @@ -40,17 +42,22 @@ import android.view.View.OnClickListener; import android.webkit.WebView; import android.widget.AdapterView; import android.widget.ArrayAdapter; +import android.widget.CheckBox; import android.widget.CheckedTextView; +import android.widget.CompoundButton; +import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.RelativeLayout; +import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.CompoundButton.OnCheckedChangeListener; import com.fsck.k9.Account; import com.fsck.k9.AccountStats; @@ -70,13 +77,18 @@ import com.fsck.k9.controller.MessagingController; import com.fsck.k9.controller.MessagingListener; import com.fsck.k9.helper.SizeFormatter; import com.fsck.k9.mail.Flag; +import com.fsck.k9.mail.ServerSettings; +import com.fsck.k9.mail.Store; +import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.store.StorageManager; +import com.fsck.k9.mail.store.WebDavStore; import com.fsck.k9.view.ColorChip; import com.fsck.k9.preferences.SettingsExporter; import com.fsck.k9.preferences.SettingsImportExportException; import com.fsck.k9.preferences.SettingsImporter; import com.fsck.k9.preferences.SettingsImporter.AccountDescription; +import com.fsck.k9.preferences.SettingsImporter.AccountDescriptionPair; import com.fsck.k9.preferences.SettingsImporter.ImportContents; import com.fsck.k9.preferences.SettingsImporter.ImportResults; @@ -542,7 +554,10 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC MessageList.actionHandle(this, searchAccount.getDescription(), searchAccount); } else { Account realAccount = (Account)account; - if (!realAccount.isAvailable(this)) { + if (!realAccount.isEnabled()) { + onActivateAccount(realAccount); + return false; + } else if (!realAccount.isAvailable(this)) { String toastText = getString(R.string.account_unavailable, account.getDescription()); Toast toast = Toast.makeText(getApplication(), toastText, Toast.LENGTH_SHORT); toast.show(); @@ -559,6 +574,311 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC return true; } + private void onActivateAccount(Account account) { + List disabledAccounts = new ArrayList(); + disabledAccounts.add(account); + promptForServerPasswords(disabledAccounts); + } + + /** + * Ask the user to enter the server passwords for disabled accounts. + * + * @param disabledAccounts + * A non-empty list of {@link Account}s to ask the user for passwords. Never + * {@code null}. + *

Note: Calling this method will modify the supplied list.

+ */ + private void promptForServerPasswords(final List disabledAccounts) { + Account account = disabledAccounts.remove(0); + PasswordPromptDialog dialog = new PasswordPromptDialog(account, disabledAccounts); + setNonConfigurationInstance(dialog); + dialog.show(this); + } + + /** + * Ask the user for the incoming/outgoing server passwords. + */ + private static class PasswordPromptDialog implements NonConfigurationInstance, TextWatcher { + private AlertDialog mDialog; + private EditText mIncomingPasswordView; + private EditText mOutgoingPasswordView; + private CheckBox mUseIncomingView; + + private Account mAccount; + private List mRemainingAccounts; + private String mIncomingPassword; + private String mOutgoingPassword; + private boolean mUseIncoming; + + /** + * Constructor + * + * @param account + * The {@link Account} to ask the server passwords for. Never {@code null}. + * @param accounts + * The (possibly empty) list of remaining accounts to ask passwords for. Never + * {@code null}. + */ + PasswordPromptDialog(Account account, List accounts) { + mAccount = account; + mRemainingAccounts = accounts; + } + + @Override + public void restore(Activity activity) { + show((Accounts) activity, true); + } + + @Override + public boolean retain() { + if (mDialog != null) { + // Retain entered passwords and checkbox state + mIncomingPassword = mIncomingPasswordView.getText().toString(); + if (mOutgoingPasswordView != null) { + mOutgoingPassword = mOutgoingPasswordView.getText().toString(); + mUseIncoming = mUseIncomingView.isChecked(); + } + + // Dismiss dialog + mDialog.dismiss(); + + // Clear all references to UI objects + mDialog = null; + mIncomingPasswordView = null; + mOutgoingPasswordView = null; + mUseIncomingView = null; + return true; + } + return false; + } + + public void show(Accounts activity) { + show(activity, false); + } + + private void show(final Accounts activity, boolean restore) { + ServerSettings incoming = Store.decodeStoreUri(mAccount.getStoreUri()); + ServerSettings outgoing = Transport.decodeTransportUri(mAccount.getTransportUri()); + + // Don't ask for the password to the outgoing server for WebDAV accounts, because + // incoming and outgoing servers are identical for this account type. + boolean configureOutgoingServer = !WebDavStore.STORE_TYPE.equals(outgoing.type); + + // Create a ScrollView that will be used as container for the whole layout + final ScrollView scrollView = new ScrollView(activity); + + // Create the dialog + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(activity.getString(R.string.settings_import_activate_account_header)); + builder.setView(scrollView); + builder.setPositiveButton(activity.getString(R.string.okay_action), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String incomingPassword = mIncomingPasswordView.getText().toString(); + String outgoingPassword = null; + if (mOutgoingPasswordView != null) { + outgoingPassword = (mUseIncomingView.isChecked()) ? + incomingPassword : mOutgoingPasswordView.getText().toString(); + } + + dialog.dismiss(); + + // Set the server passwords in the background + SetPasswordsAsyncTask asyncTask = new SetPasswordsAsyncTask(activity, mAccount, + incomingPassword, outgoingPassword, mRemainingAccounts); + activity.setNonConfigurationInstance(asyncTask); + asyncTask.execute(); + } + }); + builder.setNegativeButton(activity.getString(R.string.cancel_action), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + activity.setNonConfigurationInstance(null); + } + }); + mDialog = builder.create(); + + // Use the dialog's layout inflater so its theme is used (and not the activity's theme). + View layout = mDialog.getLayoutInflater().inflate( + R.layout.accounts_password_prompt, null); + + // Set the intro text that tells the user what to do + TextView intro = (TextView) layout.findViewById(R.id.password_prompt_intro); + String serverPasswords = activity.getResources().getQuantityString( + R.plurals.settings_import_server_passwords, + (configureOutgoingServer) ? 2 : 1); + intro.setText(activity.getString(R.string.settings_import_activate_account_intro, + mAccount.getDescription(), serverPasswords)); + + // Display the hostname of the incoming server + TextView incomingText = (TextView) layout.findViewById( + R.id.password_prompt_incoming_server); + incomingText.setText(activity.getString(R.string.settings_import_incoming_server, + incoming.host)); + + mIncomingPasswordView = (EditText) layout.findViewById(R.id.incoming_server_password); + mIncomingPasswordView.addTextChangedListener(this); + + if (configureOutgoingServer) { + // Display the hostname of the outgoing server + TextView outgoingText = (TextView) layout.findViewById( + R.id.password_prompt_outgoing_server); + outgoingText.setText(activity.getString(R.string.settings_import_outgoing_server, + outgoing.host)); + + mOutgoingPasswordView = (EditText) layout.findViewById( + R.id.outgoing_server_password); + mOutgoingPasswordView.addTextChangedListener(this); + + mUseIncomingView = (CheckBox) layout.findViewById( + R.id.use_incoming_server_password); + mUseIncomingView.setChecked(true); + mUseIncomingView.setOnCheckedChangeListener(new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + mOutgoingPasswordView.setText(null); + mOutgoingPasswordView.setEnabled(false); + } else { + mOutgoingPasswordView.setText(mIncomingPasswordView.getText()); + mOutgoingPasswordView.setEnabled(true); + } + } + }); + } else { + layout.findViewById(R.id.outgoing_server_prompt).setVisibility(View.GONE); + } + + // Add the layout to the ScrollView + scrollView.addView(layout); + + // Show the dialog + mDialog.show(); + + // Restore the contents of the password boxes and the checkbox (if the dialog was + // retained during a configuration change). + if (restore) { + mIncomingPasswordView.setText(mIncomingPassword); + if (configureOutgoingServer) { + mOutgoingPasswordView.setText(mOutgoingPassword); + mUseIncomingView.setChecked(mUseIncoming); + } + } else { + // Trigger afterTextChanged() being called + // Work around this bug: https://code.google.com/p/android/issues/detail?id=6360 + mIncomingPasswordView.setText(mIncomingPasswordView.getText()); + } + } + + @Override + public void afterTextChanged(Editable arg0) { + boolean enable = false; + // Is the password box for the incoming server password empty? + if (mIncomingPasswordView.getText().length() > 0) { + // Do we need to check the outgoing server password box? + if (mOutgoingPasswordView == null) { + enable = true; + } + // If the checkbox to use the incoming server password is checked we need to make + // sure that the password box for the outgoing server isn't empty. + else if (mUseIncomingView.isChecked() || + mOutgoingPasswordView.getText().length() > 0) { + enable = true; + } + } + + // Disable "OK" button if the user hasn't specified all necessary passwords. + mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(enable); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Not used + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Not used + } + } + + /** + * Set the incoming/outgoing server password in the background. + */ + private static class SetPasswordsAsyncTask extends ExtendedAsyncTask { + private Account mAccount; + private String mIncomingPassword; + private String mOutgoingPassword; + private List mRemainingAccounts; + + protected SetPasswordsAsyncTask(Activity activity, Account account, + String incomingPassword, String outgoingPassword, + List remainingAccounts) { + super(activity); + mAccount = account; + mIncomingPassword = incomingPassword; + mOutgoingPassword = outgoingPassword; + mRemainingAccounts = remainingAccounts; + } + + @Override + protected void showProgressDialog() { + String title = mActivity.getString(R.string.settings_import_activate_account_header); + int passwordCount = (mOutgoingPassword == null) ? 1 : 2; + String message = mActivity.getResources().getQuantityString( + R.plurals.settings_import_setting_passwords, passwordCount); + mProgressDialog = ProgressDialog.show(mActivity, title, message, true); + } + + @Override + protected Void doInBackground(Void... params) { + try { + // Set incoming server password + String storeUri = mAccount.getStoreUri(); + ServerSettings incoming = Store.decodeStoreUri(storeUri); + ServerSettings newIncoming = incoming.newPassword(mIncomingPassword); + String newStoreUri = Store.createStoreUri(newIncoming); + mAccount.setStoreUri(newStoreUri); + + if (mOutgoingPassword != null) { + // Set outgoing server password + String transportUri = mAccount.getTransportUri(); + ServerSettings outgoing = Transport.decodeTransportUri(transportUri); + ServerSettings newOutgoing = outgoing.newPassword(mOutgoingPassword); + String newTransportUri = Transport.createTransportUri(newOutgoing); + mAccount.setTransportUri(newTransportUri); + } + + // Mark account as enabled + mAccount.setEnabled(true); + + // Save the account settings + mAccount.save(Preferences.getPreferences(mContext)); + } catch (Exception e) { + Log.e(K9.LOG_TAG, "Something went while setting account passwords", e); + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + Accounts activity = (Accounts) mActivity; + + // Let the activity know that the background task is complete + activity.setNonConfigurationInstance(null); + + activity.refresh(); + removeProgressDialog(); + + if (mRemainingAccounts.size() > 0) { + activity.promptForServerPasswords(mRemainingAccounts); + } + } + } + public void onClick(View view) { if (view.getId() == R.id.next) { onAddNewAccount(); @@ -683,6 +1003,9 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC case R.id.open: onOpenAccount(mSelectedContextAccount); break; + case R.id.activate: + onActivateAccount(realAccount); + break; case R.id.check_mail: onCheckMail(realAccount); break; @@ -866,10 +1189,16 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); menu.setHeaderTitle(R.string.accounts_context_menu_title); - getMenuInflater().inflate(R.menu.accounts_context, menu); AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; BaseAccount account = mAdapter.getItem(info.position); + + if ((account instanceof Account) && !((Account) account).isEnabled()) { + getMenuInflater().inflate(R.menu.disabled_accounts_context, menu); + } else { + getMenuInflater().inflate(R.menu.accounts_context, menu); + } + if (account instanceof SearchAccount) { for (int i = 0; i < menu.size(); i++) { MenuItem item = menu.getItem(i); @@ -930,6 +1259,9 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC setNonConfigurationInstance(dialog); } + /** + * A simple dialog. + */ private static class SimpleDialog implements NonConfigurationInstance { private final int mHeaderRes; private final int mMessageRes; @@ -958,7 +1290,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC } public void show(final Accounts activity) { - final String message = activity.getString(mMessageRes, mArguments); + final String message = generateMessage(activity); final AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(mHeaderRes); @@ -969,18 +1301,105 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); activity.setNonConfigurationInstance(null); + okayAction(activity); } }); mDialog = builder.show(); } + + /** + * Returns the message the dialog should display. + * + * @param activity + * The {@code Activity} this dialog belongs to. + * + * @return The message the dialog should display + */ + protected String generateMessage(Accounts activity) { + return activity.getString(mMessageRes, mArguments); + } + + /** + * This method is called after the "OK" button was pressed. + * + * @param activity + * The {@code Activity} this dialog belongs to. + */ + protected void okayAction(Accounts activity) { + // Do nothing + } } + /** + * Shows a dialog that displays how many accounts were successfully imported. + * + * @param importResults + * The {@link ImportResults} instance returned by the {@link SettingsImporter}. + * @param filename + * The name of the settings file that was imported. + */ + private void showAccountsImportedDialog(ImportResults importResults, String filename) { + AccountsImportedDialog dialog = new AccountsImportedDialog(importResults, filename); + dialog.show(this); + setNonConfigurationInstance(dialog); + } + + /** + * A dialog that displays how many accounts were successfully imported. + */ + private static class AccountsImportedDialog extends SimpleDialog { + private ImportResults mImportResults; + private String mFilename; + + AccountsImportedDialog(ImportResults importResults, String filename) { + super(R.string.settings_import_success_header, R.string.settings_import_success); + mImportResults = importResults; + mFilename = filename; + } + + @Override + protected String generateMessage(Accounts activity) { + //TODO: display names of imported accounts (name from file *and* possibly new name) + + int imported = mImportResults.importedAccounts.size(); + String accounts = activity.getResources().getQuantityString( + R.plurals.settings_import_success, imported, imported); + return activity.getString(R.string.settings_import_success, accounts, mFilename); + } + + @Override + protected void okayAction(Accounts activity) { + Context context = activity.getApplicationContext(); + Preferences preferences = Preferences.getPreferences(context); + List disabledAccounts = new ArrayList(); + for (AccountDescriptionPair accountPair : mImportResults.importedAccounts) { + Account account = preferences.getAccount(accountPair.imported.uuid); + if (!account.isEnabled()) { + disabledAccounts.add(account); + } + } + activity.promptForServerPasswords(disabledAccounts); + } + } + + /** + * Display a dialog that lets the user select which accounts to import from the settings file. + * + * @param importContents + * The {@link ImportContents} instance returned by + * {@link SettingsImporter#getImportStreamContents(InputStream)} + * @param uri + * The (content) URI of the settings file. + */ private void showImportSelectionDialog(ImportContents importContents, Uri uri) { ImportSelectionDialog dialog = new ImportSelectionDialog(importContents, uri); dialog.show(this); setNonConfigurationInstance(dialog); } + /** + * A dialog that lets the user select which accounts to import from the settings file. + */ private static class ImportSelectionDialog implements NonConfigurationInstance { private ImportContents mImportContents; private Uri mUri; @@ -1104,6 +1523,14 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC } } + /** + * Set the {@code NonConfigurationInstance} this activity should retain on configuration + * changes. + * + * @param inst + * The {@link NonConfigurationInstance} that should be retained when + * {@link Accounts#onRetainNonConfigurationInstance()} is called. + */ private void setNonConfigurationInstance(NonConfigurationInstance inst) { mNonConfigurationInstance = inst; } @@ -1438,17 +1865,11 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC boolean globalSettings = mImportResults.globalSettings; int imported = mImportResults.importedAccounts.size(); if (success && (globalSettings || imported > 0)) { - - //TODO: display names of imported accounts (name from file *and* possibly new name) - if (imported == 0) { activity.showSimpleDialog(R.string.settings_import_success_header, R.string.settings_import_global_settings_success, filename); } else { - String importedAccounts = activity.getResources().getQuantityString( - R.plurals.settings_import_success, imported); - activity.showSimpleDialog(R.string.settings_import_success_header, - R.string.settings_import_success, importedAccounts, filename); + activity.showAccountsImportedDialog(mImportResults, filename); } activity.refresh(); diff --git a/src/com/fsck/k9/mail/ServerSettings.java b/src/com/fsck/k9/mail/ServerSettings.java index ef237e0a4..f7f127d29 100644 --- a/src/com/fsck/k9/mail/ServerSettings.java +++ b/src/com/fsck/k9/mail/ServerSettings.java @@ -125,4 +125,9 @@ public class ServerSettings { map.put(key, value); } } + + public ServerSettings newPassword(String newPassword) { + return new ServerSettings(type, host, port, connectionSecurity, authenticationType, + username, newPassword); + } } \ No newline at end of file diff --git a/src/com/fsck/k9/mail/store/ImapStore.java b/src/com/fsck/k9/mail/store/ImapStore.java index 0a86619a3..e1af45a78 100644 --- a/src/com/fsck/k9/mail/store/ImapStore.java +++ b/src/com/fsck/k9/mail/store/ImapStore.java @@ -305,6 +305,12 @@ public class ImapStore extends Store { putIfNotNull(extra, PATH_PREFIX_KEY, pathPrefix); return extra; } + + @Override + public ServerSettings newPassword(String newPassword) { + return new ImapStoreSettings(host, port, connectionSecurity, authenticationType, + username, newPassword, pathPrefix); + } } diff --git a/src/com/fsck/k9/mail/store/WebDavStore.java b/src/com/fsck/k9/mail/store/WebDavStore.java index ef9a6278e..001ed61ac 100644 --- a/src/com/fsck/k9/mail/store/WebDavStore.java +++ b/src/com/fsck/k9/mail/store/WebDavStore.java @@ -298,6 +298,12 @@ public class WebDavStore extends Store { putIfNotNull(extra, MAILBOX_PATH_KEY, mailboxPath); return extra; } + + @Override + public ServerSettings newPassword(String newPassword) { + return new WebDavStoreSettings(host, port, connectionSecurity, authenticationType, + username, newPassword, alias, path, authPath, mailboxPath); + } } diff --git a/src/com/fsck/k9/preferences/SettingsImporter.java b/src/com/fsck/k9/preferences/SettingsImporter.java index 0d280e591..82f95a886 100644 --- a/src/com/fsck/k9/preferences/SettingsImporter.java +++ b/src/com/fsck/k9/preferences/SettingsImporter.java @@ -77,7 +77,7 @@ public class SettingsImporter { } } - private static class AccountDescriptionPair { + public static class AccountDescriptionPair { public final AccountDescription original; public final AccountDescription imported;