Split message code into Local/Remote

The remote network code does not need to be aware of concepts like
Accounts etc.
This commit is contained in:
Jan Berkel 2014-12-12 03:23:32 +00:00
parent 6f4610dd5b
commit 9fd722d7cd
36 changed files with 641 additions and 589 deletions

View File

@ -29,6 +29,8 @@ import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Store; import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Folder.FolderClass; import com.fsck.k9.mail.Folder.FolderClass;
import com.fsck.k9.mail.store.RemoteStore;
import com.fsck.k9.mail.store.StoreConfig;
import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.mail.store.StorageManager;
import com.fsck.k9.mail.store.StorageManager.StorageProvider; import com.fsck.k9.mail.store.StorageManager.StorageProvider;
import com.fsck.k9.mail.store.local.LocalStore; import com.fsck.k9.mail.store.local.LocalStore;
@ -48,7 +50,7 @@ import com.larswerkman.colorpicker.ColorPicker;
* Account stores all of the settings for a single account defined by the user. It is able to save * Account stores all of the settings for a single account defined by the user. It is able to save
* and delete itself given a Preferences to work with. Each account is defined by a UUID. * and delete itself given a Preferences to work with. Each account is defined by a UUID.
*/ */
public class Account implements BaseAccount { public class Account implements BaseAccount, StoreConfig {
/** /**
* Default value for the inbox folder (never changes for POP3 and IMAP) * Default value for the inbox folder (never changes for POP3 and IMAP)
*/ */
@ -1289,11 +1291,11 @@ public class Account implements BaseAccount {
} }
public LocalStore getLocalStore() throws MessagingException { public LocalStore getLocalStore() throws MessagingException {
return Store.getLocalInstance(this, K9.app); return LocalStore.getInstance(this, K9.app);
} }
public Store getRemoteStore() throws MessagingException { public Store getRemoteStore() throws MessagingException {
return Store.getRemoteInstance(this); return RemoteStore.getInstance(this);
} }
// It'd be great if this actually went into the store implementation // It'd be great if this actually went into the store implementation

View File

@ -14,6 +14,7 @@ import android.content.SharedPreferences;
import android.util.Log; import android.util.Log;
import com.fsck.k9.mail.Store; import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.store.local.LocalStore;
import com.fsck.k9.preferences.Editor; import com.fsck.k9.preferences.Editor;
import com.fsck.k9.preferences.Storage; import com.fsck.k9.preferences.Storage;
@ -121,7 +122,7 @@ public class Preferences {
accountsInOrder.remove(account); accountsInOrder.remove(account);
} }
Store.removeAccount(account); LocalStore.removeAccount(account);
account.deleteCertificates(); account.deleteCertificates();
account.delete(this); account.delete(this);

View File

@ -5,7 +5,6 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashSet; import java.util.HashSet;
@ -76,12 +75,11 @@ import com.fsck.k9.activity.setup.Prefs;
import com.fsck.k9.activity.setup.WelcomeMessage; import com.fsck.k9.activity.setup.WelcomeMessage;
import com.fsck.k9.controller.MessagingController; import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.helper.SizeFormatter; import com.fsck.k9.helper.SizeFormatter;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.AuthType; import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.store.RemoteStore;
import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.mail.store.StorageManager;
import com.fsck.k9.mail.store.WebDavStore; import com.fsck.k9.mail.store.WebDavStore;
import com.fsck.k9.preferences.SettingsExporter; import com.fsck.k9.preferences.SettingsExporter;
@ -771,7 +769,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
} }
private void show(final Accounts activity, boolean restore) { private void show(final Accounts activity, boolean restore) {
ServerSettings incoming = Store.decodeStoreUri(mAccount.getStoreUri()); ServerSettings incoming = RemoteStore.decodeStoreUri(mAccount.getStoreUri());
ServerSettings outgoing = Transport.decodeTransportUri(mAccount.getTransportUri()); ServerSettings outgoing = Transport.decodeTransportUri(mAccount.getTransportUri());
/* /*
@ -992,9 +990,9 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
if (mIncomingPassword != null) { if (mIncomingPassword != null) {
// Set incoming server password // Set incoming server password
String storeUri = mAccount.getStoreUri(); String storeUri = mAccount.getStoreUri();
ServerSettings incoming = Store.decodeStoreUri(storeUri); ServerSettings incoming = RemoteStore.decodeStoreUri(storeUri);
ServerSettings newIncoming = incoming.newPassword(mIncomingPassword); ServerSettings newIncoming = incoming.newPassword(mIncomingPassword);
String newStoreUri = Store.createStoreUri(newIncoming); String newStoreUri = RemoteStore.createStoreUri(newIncoming);
mAccount.setStoreUri(newStoreUri); mAccount.setStoreUri(newStoreUri);
} }

View File

@ -108,6 +108,7 @@ import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.TextBody;
import com.fsck.k9.mail.internet.TextBodyBuilder; import com.fsck.k9.mail.internet.TextBodyBuilder;
import com.fsck.k9.mail.store.local.LocalAttachmentBody; import com.fsck.k9.mail.store.local.LocalAttachmentBody;
import com.fsck.k9.mail.store.local.LocalMessage;
import com.fsck.k9.mail.store.local.TempFileBody; import com.fsck.k9.mail.store.local.TempFileBody;
import com.fsck.k9.mail.store.local.TempFileMessageBody; import com.fsck.k9.mail.store.local.TempFileMessageBody;
import com.fsck.k9.view.MessageWebView; import com.fsck.k9.view.MessageWebView;
@ -435,14 +436,12 @@ public class MessageCompose extends K9Activity implements OnClickListener,
* Get intent for composing a new message as a reply to the given message. If replyAll is true * Get intent for composing a new message as a reply to the given message. If replyAll is true
* the function is reply all instead of simply reply. * the function is reply all instead of simply reply.
* @param context * @param context
* @param account
* @param message * @param message
* @param replyAll * @param replyAll
* @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message * @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message
*/ */
public static Intent getActionReplyIntent( public static Intent getActionReplyIntent(
Context context, Context context,
Account account,
Message message, Message message,
boolean replyAll, boolean replyAll,
String messageBody) { String messageBody) {
@ -468,23 +467,20 @@ public class MessageCompose extends K9Activity implements OnClickListener,
*/ */
public static void actionReply( public static void actionReply(
Context context, Context context,
Account account,
Message message, Message message,
boolean replyAll, boolean replyAll,
String messageBody) { String messageBody) {
context.startActivity(getActionReplyIntent(context, account, message, replyAll, messageBody)); context.startActivity(getActionReplyIntent(context, message, replyAll, messageBody));
} }
/** /**
* Compose a new message as a forward of the given message. * Compose a new message as a forward of the given message.
* @param context * @param context
* @param account
* @param message * @param message
* @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message * @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message
*/ */
public static void actionForward( public static void actionForward(
Context context, Context context,
Account account,
Message message, Message message,
String messageBody) { String messageBody) {
Intent i = new Intent(context, MessageCompose.class); Intent i = new Intent(context, MessageCompose.class);
@ -2646,7 +2642,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
* @param message * @param message
* The source message used to populate the various text fields. * The source message used to populate the various text fields.
*/ */
private void processSourceMessage(Message message) { private void processSourceMessage(LocalMessage message) {
try { try {
switch (mAction) { switch (mAction) {
case REPLY: case REPLY:
@ -2800,7 +2796,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
} }
} }
private void processDraftMessage(Message message) throws MessagingException { private void processDraftMessage(LocalMessage message) throws MessagingException {
String showQuotedTextMode = "NONE"; String showQuotedTextMode = "NONE";
mDraftId = MessagingController.getInstance(getApplication()).getId(message); mDraftId = MessagingController.getInstance(getApplication()).getId(message);
@ -2852,7 +2848,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
newIdentity.setSignature(k9identity.get(IdentityField.SIGNATURE)); newIdentity.setSignature(k9identity.get(IdentityField.SIGNATURE));
mSignatureChanged = true; mSignatureChanged = true;
} else { } else {
newIdentity.setSignatureUse(message.getFolder().getAccount().getSignatureUse()); newIdentity.setSignatureUse(message.getFolder().getSignatureUse());
newIdentity.setSignature(mIdentity.getSignature()); newIdentity.setSignature(mIdentity.getSignature());
} }
@ -3437,7 +3433,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
} }
updateMessageFormat(); updateMessageFormat();
} else { } else {
processSourceMessage(message); processSourceMessage((LocalMessage) message);
mSourceProcessed = true; mSourceProcessed = true;
} }
} }

View File

@ -2,6 +2,7 @@ package com.fsck.k9.activity;
import java.util.Date; import java.util.Date;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.store.local.LocalMessage;
public class MessageInfoHolder { public class MessageInfoHolder {
public String date; public String date;
@ -18,7 +19,7 @@ public class MessageInfoHolder {
public boolean forwarded; public boolean forwarded;
public boolean flagged; public boolean flagged;
public boolean dirty; public boolean dirty;
public Message message; public LocalMessage message;
public FolderInfoHolder folder; public FolderInfoHolder folder;
public boolean selected; public boolean selected;
public String account; public String account;
@ -31,7 +32,7 @@ public class MessageInfoHolder {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (o instanceof MessageInfoHolder == false) { if (!(o instanceof MessageInfoHolder)) {
return false; return false;
} }
MessageInfoHolder other = (MessageInfoHolder)o; MessageInfoHolder other = (MessageInfoHolder)o;

View File

@ -1199,17 +1199,17 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
@Override @Override
public void onForward(Message message) { public void onForward(Message message) {
MessageCompose.actionForward(this, message.getFolder().getAccount(), message, null); MessageCompose.actionForward(this, message, null);
} }
@Override @Override
public void onReply(Message message) { public void onReply(Message message) {
MessageCompose.actionReply(this, message.getFolder().getAccount(), message, false, null); MessageCompose.actionReply(this, message, false, null);
} }
@Override @Override
public void onReplyAll(Message message) { public void onReplyAll(Message message) {
MessageCompose.actionReply(this, message.getFolder().getAccount(), message, true, null); MessageCompose.actionReply(this, message, true, null);
} }
@Override @Override
@ -1400,17 +1400,17 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
@Override @Override
public void onReply(Message message, PgpData pgpData) { public void onReply(Message message, PgpData pgpData) {
MessageCompose.actionReply(this, mAccount, message, false, pgpData.getDecryptedData()); MessageCompose.actionReply(this, message, false, pgpData.getDecryptedData());
} }
@Override @Override
public void onReplyAll(Message message, PgpData pgpData) { public void onReplyAll(Message message, PgpData pgpData) {
MessageCompose.actionReply(this, mAccount, message, true, pgpData.getDecryptedData()); MessageCompose.actionReply(this, message, true, pgpData.getDecryptedData());
} }
@Override @Override
public void onForward(Message mMessage, PgpData mPgpData) { public void onForward(Message mMessage, PgpData mPgpData) {
MessageCompose.actionForward(this, mAccount, mMessage, mPgpData.getDecryptedData()); MessageCompose.actionForward(this, mMessage, mPgpData.getDecryptedData());
} }
@Override @Override

View File

@ -13,6 +13,8 @@ import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.store.local.LocalFolder;
import com.fsck.k9.mail.store.local.LocalMessage;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@ -128,13 +130,13 @@ public class MessageReference implements Parcelable {
'}'; '}';
} }
public Message restoreToLocalMessage(Context context) { public LocalMessage restoreToLocalMessage(Context context) {
try { try {
Account account = Preferences.getPreferences(context).getAccount(accountUuid); Account account = Preferences.getPreferences(context).getAccount(accountUuid);
if (account != null) { if (account != null) {
Folder folder = account.getLocalStore().getFolder(folderName); LocalFolder folder = account.getLocalStore().getFolder(folderName);
if (folder != null) { if (folder != null) {
Message message = folder.getMessage(uid); LocalMessage message = folder.getMessage(uid);
if (message != null) { if (message != null) {
return message; return message;
} else { } else {

View File

@ -38,9 +38,9 @@ import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.AuthType; import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity; import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.store.ImapStore; import com.fsck.k9.mail.store.ImapStore;
import com.fsck.k9.mail.store.RemoteStore;
import com.fsck.k9.mail.transport.SmtpTransport; import com.fsck.k9.mail.transport.SmtpTransport;
import com.fsck.k9.view.ClientCertificateSpinner; import com.fsck.k9.view.ClientCertificateSpinner;
import com.fsck.k9.view.ClientCertificateSpinner.OnClientCertificateChangedListener; import com.fsck.k9.view.ClientCertificateSpinner.OnClientCertificateChangedListener;
@ -421,7 +421,7 @@ public class AccountSetupBasics extends K9Activity
ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias); ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias);
ServerSettings transportServer = new ServerSettings(SmtpTransport.TRANSPORT_TYPE, "mail." + domain, -1, ServerSettings transportServer = new ServerSettings(SmtpTransport.TRANSPORT_TYPE, "mail." + domain, -1,
ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias); ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias);
String storeUri = Store.createStoreUri(storeServer); String storeUri = RemoteStore.createStoreUri(storeServer);
String transportUri = Transport.createTransportUri(transportServer); String transportUri = Transport.createTransportUri(transportServer);
mAccount.setStoreUri(storeUri); mAccount.setStoreUri(storeUri);
mAccount.setTransportUri(transportUri); mAccount.setTransportUri(transportUri);

View File

@ -27,6 +27,7 @@ import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.store.ImapStore; import com.fsck.k9.mail.store.ImapStore;
import com.fsck.k9.mail.store.Pop3Store; import com.fsck.k9.mail.store.Pop3Store;
import com.fsck.k9.mail.store.RemoteStore;
import com.fsck.k9.mail.store.WebDavStore; import com.fsck.k9.mail.store.WebDavStore;
import com.fsck.k9.mail.store.ImapStore.ImapStoreSettings; import com.fsck.k9.mail.store.ImapStore.ImapStoreSettings;
import com.fsck.k9.mail.store.WebDavStore.WebDavStoreSettings; import com.fsck.k9.mail.store.WebDavStore.WebDavStoreSettings;
@ -163,7 +164,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
} }
try { try {
ServerSettings settings = Store.decodeStoreUri(mAccount.getStoreUri()); ServerSettings settings = RemoteStore.decodeStoreUri(mAccount.getStoreUri());
if (savedInstanceState == null) { if (savedInstanceState == null) {
// The first item is selected if settings.authenticationType is null or is not in mAuthTypeAdapter // The first item is selected if settings.authenticationType is null or is not in mAuthTypeAdapter
@ -610,7 +611,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
ServerSettings settings = new ServerSettings(mStoreType, host, port, ServerSettings settings = new ServerSettings(mStoreType, host, port,
connectionSecurity, authType, username, password, clientCertificateAlias, extra); connectionSecurity, authType, username, password, clientCertificateAlias, extra);
mAccount.setStoreUri(Store.createStoreUri(settings)); mAccount.setStoreUri(RemoteStore.createStoreUri(settings));
mAccount.setCompression(Account.TYPE_MOBILE, mCompressionMobile.isChecked()); mAccount.setCompression(Account.TYPE_MOBILE, mCompressionMobile.isChecked());
mAccount.setCompression(Account.TYPE_WIFI, mCompressionWifi.isChecked()); mAccount.setCompression(Account.TYPE_WIFI, mCompressionWifi.isChecked());

View File

@ -123,13 +123,11 @@ public class EmailProviderCache {
} }
} }
public void hideMessages(List<Message> messages) { public void hideMessages(List<LocalMessage> messages) {
synchronized (mHiddenMessageCache) { synchronized (mHiddenMessageCache) {
for (Message message : messages) { for (LocalMessage message : messages) {
LocalMessage localMessage = (LocalMessage) message; long messageId = message.getId();
long messageId = localMessage.getId(); mHiddenMessageCache.put(messageId, message.getFolder().getId());
long folderId = ((LocalFolder) localMessage.getFolder()).getId();
mHiddenMessageCache.put(messageId, folderId);
} }
} }

View File

@ -3,10 +3,10 @@ package com.fsck.k9.controller;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
public interface MessageRetrievalListener { public interface MessageRetrievalListener<T extends Message> {
public void messageStarted(String uid, int number, int ofTotal); public void messageStarted(String uid, int number, int ofTotal);
public void messageFinished(Message message, int number, int ofTotal); public void messageFinished(T message, int number, int ofTotal);
/** /**
* FIXME <strong>this method is almost never invoked by various Stores! Don't rely on it unless fixed!!</strong> * FIXME <strong>this method is almost never invoked by various Stores! Don't rely on it unless fixed!!</strong>

View File

@ -312,7 +312,7 @@ public class MessagingController implements Runnable {
private static final Set<Flag> SYNC_FLAGS = EnumSet.of(Flag.SEEN, Flag.FLAGGED, Flag.ANSWERED, Flag.FORWARDED); private static final Set<Flag> SYNC_FLAGS = EnumSet.of(Flag.SEEN, Flag.FLAGGED, Flag.ANSWERED, Flag.FORWARDED);
private void suppressMessages(Account account, List<Message> messages) { private void suppressMessages(Account account, List<LocalMessage> messages) {
EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(), EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(),
mApplication.getApplicationContext()); mApplication.getApplicationContext());
cache.hideMessages(messages); cache.hideMessages(messages);
@ -324,13 +324,11 @@ public class MessagingController implements Runnable {
cache.unhideMessages(messages); cache.unhideMessages(messages);
} }
private boolean isMessageSuppressed(Account account, Message message) { private boolean isMessageSuppressed(LocalMessage message) {
LocalMessage localMessage = (LocalMessage) message; long messageId = message.getId();
String accountUuid = account.getUuid(); long folderId = message.getFolder().getId();
long messageId = localMessage.getId();
long folderId = ((LocalFolder) localMessage.getFolder()).getId();
EmailProviderCache cache = EmailProviderCache.getCache(accountUuid, EmailProviderCache cache = EmailProviderCache.getCache(message.getFolder().getUuid(),
mApplication.getApplicationContext()); mApplication.getApplicationContext());
return cache.isMessageHidden(messageId, folderId); return cache.isMessageHidden(messageId, folderId);
} }
@ -690,15 +688,15 @@ public class MessagingController implements Runnable {
} }
// Collecting statistics of the search result // Collecting statistics of the search result
MessageRetrievalListener retrievalListener = new MessageRetrievalListener() { MessageRetrievalListener retrievalListener = new MessageRetrievalListener<LocalMessage>() {
@Override @Override
public void messageStarted(String message, int number, int ofTotal) {} public void messageStarted(String message, int number, int ofTotal) {}
@Override @Override
public void messagesFinished(int number) {} public void messagesFinished(int number) {}
@Override @Override
public void messageFinished(Message message, int number, int ofTotal) { public void messageFinished(LocalMessage message, int number, int ofTotal) {
if (!isMessageSuppressed(message.getFolder().getAccount(), message)) { if (!isMessageSuppressed(message)) {
List<Message> messages = new ArrayList<Message>(); List<LocalMessage> messages = new ArrayList<LocalMessage>();
messages.add(message); messages.add(message);
stats.unreadMessageCount += (!message.isSet(Flag.SEEN)) ? 1 : 0; stats.unreadMessageCount += (!message.isSet(Flag.SEEN)) ? 1 : 0;
@ -762,7 +760,7 @@ public class MessagingController implements Runnable {
final Account acct = Preferences.getPreferences(mApplication.getApplicationContext()).getAccount(acctUuid); final Account acct = Preferences.getPreferences(mApplication.getApplicationContext()).getAccount(acctUuid);
if (listener != null) { if (listener != null) {
listener.remoteSearchStarted(acct, folderName); listener.remoteSearchStarted(folderName);
} }
List<Message> extraResults = new ArrayList<Message>(); List<Message> extraResults = new ArrayList<Message>();
@ -791,7 +789,7 @@ public class MessagingController implements Runnable {
messages.clear(); messages.clear();
if (listener != null) { if (listener != null) {
listener.remoteSearchServerQueryComplete(acct, folderName, remoteMessages.size()); listener.remoteSearchServerQueryComplete(folderName, remoteMessages.size(), acct.getRemoteSearchNumResults());
} }
Collections.sort(remoteMessages, new UidReverseComparator()); Collections.sort(remoteMessages, new UidReverseComparator());
@ -811,13 +809,13 @@ public class MessagingController implements Runnable {
} else { } else {
Log.e(K9.LOG_TAG, "Could not complete remote search", e); Log.e(K9.LOG_TAG, "Could not complete remote search", e);
if (listener != null) { if (listener != null) {
listener.remoteSearchFailed(acct, null, e.getMessage()); listener.remoteSearchFailed(null, e.getMessage());
} }
addErrorMessage(acct, null, e); addErrorMessage(acct, null, e);
} }
} finally { } finally {
if (listener != null) { if (listener != null) {
listener.remoteSearchFinished(acct, folderName, 0, extraResults); listener.remoteSearchFinished(folderName, 0, acct.getRemoteSearchNumResults(), extraResults);
} }
} }
@ -878,7 +876,7 @@ public class MessagingController implements Runnable {
} }
if (listener != null) { if (listener != null) {
listener.remoteSearchAddMessage(remoteFolder.getAccount(), remoteFolder.getName(), localMsg, i, messages.size()); listener.remoteSearchAddMessage(remoteFolder.getName(), localMsg, i, messages.size());
} }
} }
} }
@ -1455,7 +1453,7 @@ public class MessagingController implements Runnable {
} }
} }
private void fetchUnsyncedMessages(final Account account, final Folder remoteFolder, private <T extends Message> void fetchUnsyncedMessages(final Account account, final Folder<T> remoteFolder,
final LocalFolder localFolder, final LocalFolder localFolder,
List<Message> unsyncedMessages, List<Message> unsyncedMessages,
final List<Message> smallMessages, final List<Message> smallMessages,
@ -1473,9 +1471,9 @@ public class MessagingController implements Runnable {
final List<Message> chunk = new ArrayList<Message>(UNSYNC_CHUNK_SIZE); final List<Message> chunk = new ArrayList<Message>(UNSYNC_CHUNK_SIZE);
remoteFolder.fetch(unsyncedMessages, fp, remoteFolder.fetch(unsyncedMessages, fp,
new MessageRetrievalListener() { new MessageRetrievalListener<T>() {
@Override @Override
public void messageFinished(Message message, int number, int ofTotal) { public void messageFinished(T message, int number, int ofTotal) {
try { try {
String newPushState = remoteFolder.getNewPushState(localFolder.getPushState(), message); String newPushState = remoteFolder.getNewPushState(localFolder.getPushState(), message);
if (newPushState != null) { if (newPushState != null) {
@ -1564,7 +1562,7 @@ public class MessagingController implements Runnable {
localFolder.appendMessages(messages); localFolder.appendMessages(messages);
for (final Message message : messages) { for (final Message message : messages) {
final Message localMessage = localFolder.getMessage(message.getUid()); final LocalMessage localMessage = localFolder.getMessage(message.getUid());
syncFlags(localMessage, message); syncFlags(localMessage, message);
if (K9.DEBUG) if (K9.DEBUG)
Log.v(K9.LOG_TAG, "About to notify listeners that we got a new unsynced message " Log.v(K9.LOG_TAG, "About to notify listeners that we got a new unsynced message "
@ -1592,9 +1590,9 @@ public class MessagingController implements Runnable {
return true; return true;
} }
private void downloadSmallMessages(final Account account, final Folder remoteFolder, private <T extends Message> void downloadSmallMessages(final Account account, final Folder<T> remoteFolder,
final LocalFolder localFolder, final LocalFolder localFolder,
List<Message> smallMessages, List<T> smallMessages,
final AtomicInteger progress, final AtomicInteger progress,
final int unreadBeforeStart, final int unreadBeforeStart,
final AtomicInteger newMessages, final AtomicInteger newMessages,
@ -1608,9 +1606,9 @@ public class MessagingController implements Runnable {
Log.d(K9.LOG_TAG, "SYNC: Fetching small messages for folder " + folder); Log.d(K9.LOG_TAG, "SYNC: Fetching small messages for folder " + folder);
remoteFolder.fetch(smallMessages, remoteFolder.fetch(smallMessages,
fp, new MessageRetrievalListener() { fp, new MessageRetrievalListener<T>() {
@Override @Override
public void messageFinished(final Message message, int number, int ofTotal) { public void messageFinished(final T message, int number, int ofTotal) {
try { try {
if (!shouldImportMessage(account, folder, message, progress, earliestDate)) { if (!shouldImportMessage(account, folder, message, progress, earliestDate)) {
@ -1671,9 +1669,9 @@ public class MessagingController implements Runnable {
private void downloadLargeMessages(final Account account, final Folder remoteFolder, private <T extends Message> void downloadLargeMessages(final Account account, final Folder<T> remoteFolder,
final LocalFolder localFolder, final LocalFolder localFolder,
List<Message> largeMessages, List<T> largeMessages,
final AtomicInteger progress, final AtomicInteger progress,
final int unreadBeforeStart, final int unreadBeforeStart,
final AtomicInteger newMessages, final AtomicInteger newMessages,
@ -1821,11 +1819,11 @@ public class MessagingController implements Runnable {
remoteFolder.fetch(undeletedMessages, fp, null); remoteFolder.fetch(undeletedMessages, fp, null);
for (Message remoteMessage : syncFlagMessages) { for (Message remoteMessage : syncFlagMessages) {
Message localMessage = localFolder.getMessage(remoteMessage.getUid()); LocalMessage localMessage = localFolder.getMessage(remoteMessage.getUid());
boolean messageChanged = syncFlags(localMessage, remoteMessage); boolean messageChanged = syncFlags(localMessage, remoteMessage);
if (messageChanged) { if (messageChanged) {
boolean shouldBeNotifiedOf = false; boolean shouldBeNotifiedOf = false;
if (localMessage.isSet(Flag.DELETED) || isMessageSuppressed(account, localMessage)) { if (localMessage.isSet(Flag.DELETED) || isMessageSuppressed(localMessage)) {
for (MessagingListener l : getListeners()) { for (MessagingListener l : getListeners()) {
l.synchronizeMailboxRemovedMessage(account, folder, localMessage); l.synchronizeMailboxRemovedMessage(account, folder, localMessage);
} }
@ -1859,13 +1857,13 @@ public class MessagingController implements Runnable {
} }
} }
private boolean syncFlags(Message localMessage, Message remoteMessage) throws MessagingException { private boolean syncFlags(LocalMessage localMessage, Message remoteMessage) throws MessagingException {
boolean messageChanged = false; boolean messageChanged = false;
if (localMessage == null || localMessage.isSet(Flag.DELETED)) { if (localMessage == null || localMessage.isSet(Flag.DELETED)) {
return false; return false;
} }
if (remoteMessage.isSet(Flag.DELETED)) { if (remoteMessage.isSet(Flag.DELETED)) {
if (localMessage.getFolder().getAccount().syncRemoteDeletions()) { if (localMessage.getFolder().syncRemoteDeletions()) {
localMessage.setFlag(Flag.DELETED, true); localMessage.setFlag(Flag.DELETED, true);
messageChanged = true; messageChanged = true;
} }
@ -2857,7 +2855,7 @@ public class MessagingController implements Runnable {
* @param newState * @param newState
* {@code true}, if the flag should be set. {@code false} if it should be removed. * {@code true}, if the flag should be set. {@code false} if it should be removed.
*/ */
public void setFlag(Account account, String folderName, List<Message> messages, Flag flag, public void setFlag(Account account, String folderName, List<? extends Message> messages, Flag flag,
boolean newState) { boolean newState) {
// TODO: Put this into the background, but right now some callers depend on the message // TODO: Put this into the background, but right now some callers depend on the message
// objects being modified right after this method returns. // objects being modified right after this method returns.
@ -3775,7 +3773,7 @@ public class MessagingController implements Runnable {
} }
} }
public void moveMessages(final Account account, final String srcFolder, public void moveMessages(final Account account, final String srcFolder,
final List<Message> messages, final String destFolder, final List<LocalMessage> messages, final String destFolder,
final MessagingListener listener) { final MessagingListener listener) {
suppressMessages(account, messages); suppressMessages(account, messages);
@ -3790,7 +3788,7 @@ public class MessagingController implements Runnable {
} }
public void moveMessagesInThread(final Account account, final String srcFolder, public void moveMessagesInThread(final Account account, final String srcFolder,
final List<Message> messages, final String destFolder) { final List<LocalMessage> messages, final String destFolder) {
suppressMessages(account, messages); suppressMessages(account, messages);
@ -3808,14 +3806,14 @@ public class MessagingController implements Runnable {
}); });
} }
public void moveMessage(final Account account, final String srcFolder, final Message message, public void moveMessage(final Account account, final String srcFolder, final LocalMessage message,
final String destFolder, final MessagingListener listener) { final String destFolder, final MessagingListener listener) {
moveMessages(account, srcFolder, Collections.singletonList(message), destFolder, listener); moveMessages(account, srcFolder, Collections.singletonList(message), destFolder, listener);
} }
public void copyMessages(final Account account, final String srcFolder, public void copyMessages(final Account account, final String srcFolder,
final List<Message> messages, final String destFolder, final List<? extends Message> messages, final String destFolder,
final MessagingListener listener) { final MessagingListener listener) {
putBackground("copyMessages", null, new Runnable() { putBackground("copyMessages", null, new Runnable() {
@ -3828,7 +3826,7 @@ public class MessagingController implements Runnable {
} }
public void copyMessagesInThread(final Account account, final String srcFolder, public void copyMessagesInThread(final Account account, final String srcFolder,
final List<Message> messages, final String destFolder) { final List<? extends Message> messages, final String destFolder) {
putBackground("copyMessagesInThread", null, new Runnable() { putBackground("copyMessagesInThread", null, new Runnable() {
@Override @Override
@ -3851,7 +3849,7 @@ public class MessagingController implements Runnable {
} }
private void moveOrCopyMessageSynchronous(final Account account, final String srcFolder, private void moveOrCopyMessageSynchronous(final Account account, final String srcFolder,
final List<Message> inMessages, final String destFolder, final boolean isCopy, final List<? extends Message> inMessages, final String destFolder, final boolean isCopy,
MessagingListener listener) { MessagingListener listener) {
try { try {
@ -3962,7 +3960,7 @@ public class MessagingController implements Runnable {
localFolder.open(Folder.OPEN_MODE_RW); localFolder.open(Folder.OPEN_MODE_RW);
String uid = localFolder.getMessageUidById(id); String uid = localFolder.getMessageUidById(id);
if (uid != null) { if (uid != null) {
Message message = localFolder.getMessage(uid); LocalMessage message = localFolder.getMessage(uid);
if (message != null) { if (message != null) {
deleteMessages(Collections.singletonList(message), null); deleteMessages(Collections.singletonList(message), null);
} }
@ -3974,7 +3972,7 @@ public class MessagingController implements Runnable {
} }
} }
public void deleteThreads(final List<Message> messages) { public void deleteThreads(final List<LocalMessage> messages) {
actOnMessages(messages, new MessageActor() { actOnMessages(messages, new MessageActor() {
@Override @Override
@ -4006,7 +4004,7 @@ public class MessagingController implements Runnable {
} }
} }
public List<Message> collectMessagesInThreads(Account account, List<Message> messages) public List<Message> collectMessagesInThreads(Account account, List<? extends Message> messages)
throws MessagingException { throws MessagingException {
LocalStore localStore = account.getLocalStore(); LocalStore localStore = account.getLocalStore();
@ -4025,7 +4023,7 @@ public class MessagingController implements Runnable {
return messagesInThreads; return messagesInThreads;
} }
public void deleteMessages(final List<Message> messages, final MessagingListener listener) { public void deleteMessages(final List<LocalMessage> messages, final MessagingListener listener) {
actOnMessages(messages, new MessageActor() { actOnMessages(messages, new MessageActor() {
@Override @Override
@ -5685,15 +5683,15 @@ public class MessagingController implements Runnable {
} }
private void actOnMessages(List<Message> messages, MessageActor actor) { private void actOnMessages(List<LocalMessage> messages, MessageActor actor) {
Map<Account, Map<Folder, List<Message>>> accountMap = new HashMap<Account, Map<Folder, List<Message>>>(); Map<Account, Map<Folder, List<Message>>> accountMap = new HashMap<Account, Map<Folder, List<Message>>>();
for (Message message : messages) { for (LocalMessage message : messages) {
if ( message == null) { if ( message == null) {
continue; continue;
} }
Folder folder = message.getFolder(); Folder folder = message.getFolder();
Account account = folder.getAccount(); Account account = message.getAccount();
Map<Folder, List<Message>> folderMap = accountMap.get(account); Map<Folder, List<Message>> folderMap = accountMap.get(account);
if (folderMap == null) { if (folderMap == null) {

View File

@ -11,6 +11,7 @@ import com.fsck.k9.BaseAccount;
import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.Part; import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.store.local.LocalMessage;
/** /**
* Defines the interface that {@link MessagingController} will use to callback to requesters. * Defines the interface that {@link MessagingController} will use to callback to requesters.
@ -42,10 +43,8 @@ public class MessagingListener {
public void listLocalMessagesStarted(Account account, String folder) {} public void listLocalMessagesStarted(Account account, String folder) {}
public void listLocalMessages(Account account, String folder, Message[] messages) {}
public void listLocalMessagesAddMessages(Account account, String folder, public void listLocalMessagesAddMessages(Account account, String folder,
List<Message> messages) {} List<LocalMessage> messages) {}
public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {} public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {}
@ -156,10 +155,9 @@ public class MessagingListener {
/** /**
* Called when a remote search is started * Called when a remote search is started
* *
* @param acct
* @param folder * @param folder
*/ */
public void remoteSearchStarted(Account acct, String folder) {} public void remoteSearchStarted(String folder) {}
/** /**
@ -167,35 +165,30 @@ public class MessagingListener {
* *
* @param numResults * @param numResults
*/ */
public void remoteSearchServerQueryComplete(Account account, String folderName, int numResults) { } public void remoteSearchServerQueryComplete(String folderName, int numResults, int maxResults) { }
/** /**
* Called when a new result message is available for a remote search * Called when a new result message is available for a remote search
* Can assume headers have been downloaded, but potentially not body. * Can assume headers have been downloaded, but potentially not body.
* @param account
* @param folder * @param folder
* @param message * @param message
*/ */
public void remoteSearchAddMessage(Account account, String folder, Message message, int numDone, int numTotal) { } public void remoteSearchAddMessage(String folder, Message message, int numDone, int numTotal) { }
/** /**
* Called when Remote Search is fully complete * Called when Remote Search is fully complete
*
* @param acct
* @param folder * @param folder
* @param numResults * @param numResults
*/ */
public void remoteSearchFinished(Account acct, String folder, int numResults, List<Message> extraResults) {} public void remoteSearchFinished(String folder, int numResults, int maxResults, List<Message> extraResults) {}
/** /**
* Called when there was a problem with a remote search operation. * Called when there was a problem with a remote search operation.
*
* @param acct
* @param folder * @param folder
* @param err * @param err
*/ */
public void remoteSearchFailed(Account acct, String folder, String err) { } public void remoteSearchFailed(String folder, String err) { }
/** /**
* General notification messages subclasses can override to be notified that the controller * General notification messages subclasses can override to be notified that the controller

View File

@ -89,6 +89,7 @@ import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.store.local.LocalFolder; import com.fsck.k9.mail.store.local.LocalFolder;
import com.fsck.k9.mail.store.local.LocalMessage;
import com.fsck.k9.mail.store.local.LocalStore; import com.fsck.k9.mail.store.local.LocalStore;
import com.fsck.k9.provider.EmailProvider; import com.fsck.k9.provider.EmailProvider;
import com.fsck.k9.provider.EmailProvider.MessageColumns; import com.fsck.k9.provider.EmailProvider.MessageColumns;
@ -427,7 +428,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* Relevant messages for the current context when we have to remember the chosen messages * Relevant messages for the current context when we have to remember the chosen messages
* between user interactions (e.g. selecting a folder for move operation). * between user interactions (e.g. selecting a folder for move operation).
*/ */
private List<Message> mActiveMessages; private List<LocalMessage> mActiveMessages;
/* package visibility for faster inner class access */ /* package visibility for faster inner class access */
MessageHelper mMessageHelper; MessageHelper mMessageHelper;
@ -1035,7 +1036,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
return null; return null;
} }
private Folder getFolderById(Account account, long folderId) { private LocalFolder getFolderById(Account account, long folderId) {
try { try {
LocalStore localStore = account.getLocalStore(); LocalStore localStore = account.getLocalStore();
LocalFolder localFolder = localStore.getFolderById(folderId); LocalFolder localFolder = localStore.getFolderById(folderId);
@ -1309,11 +1310,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
changeSort(sorts[curIndex]); changeSort(sorts[curIndex]);
} }
private void onDelete(Message message) { private void onDelete(LocalMessage message) {
onDelete(Collections.singletonList(message)); onDelete(Collections.singletonList(message));
} }
private void onDelete(List<Message> messages) { private void onDelete(List<LocalMessage> messages) {
if (K9.confirmDelete()) { if (K9.confirmDelete()) {
// remember the message selection for #onCreateDialog(int) // remember the message selection for #onCreateDialog(int)
mActiveMessages = messages; mActiveMessages = messages;
@ -1323,7 +1324,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
} }
} }
private void onDeleteConfirmed(List<Message> messages) { private void onDeleteConfirmed(List<LocalMessage> messages) {
if (mThreadedList) { if (mThreadedList) {
mController.deleteThreads(messages); mController.deleteThreads(messages);
} else { } else {
@ -1345,15 +1346,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
} }
final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER); final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER);
final List<Message> messages = mActiveMessages; final List<LocalMessage> messages = mActiveMessages;
if (destFolderName != null) { if (destFolderName != null) {
mActiveMessages = null; // don't need it any more mActiveMessages = null; // don't need it any more
if (messages.size() > 0) { if (messages.size() > 0) {
Account account = messages.get(0).getFolder().getAccount(); messages.get(0).getFolder().setLastSelectedFolderName(destFolderName);
account.setLastSelectedFolderName(destFolderName);
} }
switch (requestCode) { switch (requestCode) {
@ -1539,7 +1539,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
break; break;
} }
case R.id.delete: { case R.id.delete: {
Message message = getMessageAtPosition(adapterPosition); LocalMessage message = getMessageAtPosition(adapterPosition);
onDelete(message); onDelete(message);
break; break;
} }
@ -1562,23 +1562,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
// only if the account supports this // only if the account supports this
case R.id.archive: { case R.id.archive: {
Message message = getMessageAtPosition(adapterPosition); onArchive(getMessageAtPosition(adapterPosition));
onArchive(message);
break; break;
} }
case R.id.spam: { case R.id.spam: {
Message message = getMessageAtPosition(adapterPosition); onSpam(getMessageAtPosition(adapterPosition));
onSpam(message);
break; break;
} }
case R.id.move: { case R.id.move: {
Message message = getMessageAtPosition(adapterPosition); onMove(getMessageAtPosition(adapterPosition));
onMove(message);
break; break;
} }
case R.id.copy: { case R.id.copy: {
Message message = getMessageAtPosition(adapterPosition); onCopy(getMessageAtPosition(adapterPosition));
onCopy(message);
break; break;
} }
} }
@ -1711,7 +1707,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
class MessageListActivityListener extends ActivityListener { class MessageListActivityListener extends ActivityListener {
@Override @Override
public void remoteSearchFailed(Account acct, String folder, final String err) { public void remoteSearchFailed(String folder, final String err) {
mHandler.post(new Runnable() { mHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -1725,7 +1721,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
} }
@Override @Override
public void remoteSearchStarted(Account acct, String folder) { public void remoteSearchStarted(String folder) {
mHandler.progress(true); mHandler.progress(true);
mHandler.updateFooter(mContext.getString(R.string.remote_search_sending_query)); mHandler.updateFooter(mContext.getString(R.string.remote_search_sending_query));
} }
@ -1736,12 +1732,12 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
} }
@Override @Override
public void remoteSearchFinished(Account acct, String folder, int numResults, List<Message> extraResults) { public void remoteSearchFinished(String folder, int numResults, int maxResults, List<Message> extraResults) {
mHandler.progress(false); mHandler.progress(false);
mHandler.remoteSearchFinished(); mHandler.remoteSearchFinished();
mExtraSearchResults = extraResults; mExtraSearchResults = extraResults;
if (extraResults != null && extraResults.size() > 0) { if (extraResults != null && extraResults.size() > 0) {
mHandler.updateFooter(String.format(mContext.getString(R.string.load_more_messages_fmt), acct.getRemoteSearchNumResults())); mHandler.updateFooter(String.format(mContext.getString(R.string.load_more_messages_fmt), maxResults));
} else { } else {
mHandler.updateFooter(""); mHandler.updateFooter("");
} }
@ -1750,11 +1746,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
} }
@Override @Override
public void remoteSearchServerQueryComplete(Account account, String folderName, int numResults) { public void remoteSearchServerQueryComplete(String folderName, int numResults, int maxResults) {
mHandler.progress(true); mHandler.progress(true);
if (account != null && account.getRemoteSearchNumResults() != 0 && numResults > account.getRemoteSearchNumResults()) { if (maxResults != 0 && numResults > maxResults) {
mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading_limited, mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading_limited,
account.getRemoteSearchNumResults(), numResults)); maxResults, numResults));
} else { } else {
mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading, numResults)); mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading, numResults));
} }
@ -2416,7 +2412,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
computeBatchDirection(); computeBatchDirection();
} }
private void onMove(Message message) { private void onMove(LocalMessage message) {
onMove(Collections.singletonList(message)); onMove(Collections.singletonList(message));
} }
@ -2426,7 +2422,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param messages * @param messages
* Never {@code null}. * Never {@code null}.
*/ */
private void onMove(List<Message> messages) { private void onMove(List<LocalMessage> messages) {
if (!checkCopyOrMovePossible(messages, FolderOperation.MOVE)) { if (!checkCopyOrMovePossible(messages, FolderOperation.MOVE)) {
return; return;
} }
@ -2440,12 +2436,13 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
folder = null; folder = null;
} }
Account account = messages.get(0).getFolder().getAccount();
displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_MOVE, account, folder, messages); displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_MOVE, folder,
messages.get(0).getFolder().getUuid(), null,
messages);
} }
private void onCopy(Message message) { private void onCopy(LocalMessage message) {
onCopy(Collections.singletonList(message)); onCopy(Collections.singletonList(message));
} }
@ -2455,7 +2452,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param messages * @param messages
* Never {@code null}. * Never {@code null}.
*/ */
private void onCopy(List<Message> messages) { private void onCopy(List<LocalMessage> messages) {
if (!checkCopyOrMovePossible(messages, FolderOperation.COPY)) { if (!checkCopyOrMovePossible(messages, FolderOperation.COPY)) {
return; return;
} }
@ -2469,7 +2466,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
folder = null; folder = null;
} }
displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_COPY, mAccount, folder, messages); displayFolderChoice(ACTIVITY_CHOOSE_FOLDER_COPY, folder,
messages.get(0).getFolder().getUuid(),
null,
messages);
} }
/** /**
@ -2486,12 +2486,13 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* *
* @see #startActivityForResult(Intent, int) * @see #startActivityForResult(Intent, int)
*/ */
private void displayFolderChoice(int requestCode, Account account, Folder folder, private void displayFolderChoice(int requestCode, Folder folder,
List<Message> messages) { String accountId, String lastSelectedFolderName,
List<LocalMessage> messages) {
Intent intent = new Intent(getActivity(), ChooseFolder.class); Intent intent = new Intent(getActivity(), ChooseFolder.class);
intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, account.getUuid()); intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, accountId);
intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, account.getLastSelectedFolderName()); intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, lastSelectedFolderName);
if (folder == null) { if (folder == null) {
intent.putExtra(ChooseFolder.EXTRA_SHOW_CURRENT, "yes"); intent.putExtra(ChooseFolder.EXTRA_SHOW_CURRENT, "yes");
@ -2504,14 +2505,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
startActivityForResult(intent, requestCode); startActivityForResult(intent, requestCode);
} }
private void onArchive(final Message message) { private void onArchive(final LocalMessage message) {
onArchive(Collections.singletonList(message)); onArchive(Collections.singletonList(message));
} }
private void onArchive(final List<Message> messages) { private void onArchive(final List<LocalMessage> messages) {
Map<Account, List<Message>> messagesByAccount = groupMessagesByAccount(messages); Map<Account, List<LocalMessage>> messagesByAccount = groupMessagesByAccount(messages);
for (Entry<Account, List<Message>> entry : messagesByAccount.entrySet()) { for (Entry<Account, List<LocalMessage>> entry : messagesByAccount.entrySet()) {
Account account = entry.getKey(); Account account = entry.getKey();
String archiveFolder = account.getArchiveFolderName(); String archiveFolder = account.getArchiveFolderName();
@ -2521,14 +2522,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
} }
} }
private Map<Account, List<Message>> groupMessagesByAccount(final List<Message> messages) { private Map<Account, List<LocalMessage>> groupMessagesByAccount(final List<LocalMessage> messages) {
Map<Account, List<Message>> messagesByAccount = new HashMap<Account, List<Message>>(); Map<Account, List<LocalMessage>> messagesByAccount = new HashMap<Account, List<LocalMessage>>();
for (Message message : messages) { for (LocalMessage message : messages) {
Account account = message.getFolder().getAccount(); Account account = message.getAccount();
List<Message> msgList = messagesByAccount.get(account); List<LocalMessage> msgList = messagesByAccount.get(account);
if (msgList == null) { if (msgList == null) {
msgList = new ArrayList<Message>(); msgList = new ArrayList<LocalMessage>();
messagesByAccount.put(account, msgList); messagesByAccount.put(account, msgList);
} }
@ -2537,7 +2538,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
return messagesByAccount; return messagesByAccount;
} }
private void onSpam(Message message) { private void onSpam(LocalMessage message) {
onSpam(Collections.singletonList(message)); onSpam(Collections.singletonList(message));
} }
@ -2547,7 +2548,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param messages * @param messages
* The messages to move to the spam folder. Never {@code null}. * The messages to move to the spam folder. Never {@code null}.
*/ */
private void onSpam(List<Message> messages) { private void onSpam(List<LocalMessage> messages) {
if (K9.confirmSpam()) { if (K9.confirmSpam()) {
// remember the message selection for #onCreateDialog(int) // remember the message selection for #onCreateDialog(int)
mActiveMessages = messages; mActiveMessages = messages;
@ -2557,10 +2558,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
} }
} }
private void onSpamConfirmed(List<Message> messages) { private void onSpamConfirmed(List<LocalMessage> messages) {
Map<Account, List<Message>> messagesByAccount = groupMessagesByAccount(messages); Map<Account, List<LocalMessage>> messagesByAccount = groupMessagesByAccount(messages);
for (Entry<Account, List<Message>> entry : messagesByAccount.entrySet()) { for (Entry<Account, List<LocalMessage>> entry : messagesByAccount.entrySet()) {
Account account = entry.getKey(); Account account = entry.getKey();
String spamFolder = account.getSpamFolderName(); String spamFolder = account.getSpamFolderName();
@ -2584,7 +2585,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* *
* @return {@code true}, if operation is possible. * @return {@code true}, if operation is possible.
*/ */
private boolean checkCopyOrMovePossible(final List<Message> messages, private boolean checkCopyOrMovePossible(final List<LocalMessage> messages,
final FolderOperation operation) { final FolderOperation operation) {
if (messages.isEmpty()) { if (messages.isEmpty()) {
@ -2592,11 +2593,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
} }
boolean first = true; boolean first = true;
for (final Message message : messages) { for (final LocalMessage message : messages) {
if (first) { if (first) {
first = false; first = false;
// account check // account check
final Account account = message.getFolder().getAccount(); final Account account = message.getAccount();
if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(account)) || if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(account)) ||
(operation == FolderOperation.COPY && !mController.isCopyCapable(account))) { (operation == FolderOperation.COPY && !mController.isCopyCapable(account))) {
return false; return false;
@ -2622,7 +2623,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param destination * @param destination
* The name of the destination folder. Never {@code null}. * The name of the destination folder. Never {@code null}.
*/ */
private void copy(List<Message> messages, final String destination) { private void copy(List<LocalMessage> messages, final String destination) {
copyOrMove(messages, destination, FolderOperation.COPY); copyOrMove(messages, destination, FolderOperation.COPY);
} }
@ -2634,7 +2635,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param destination * @param destination
* The name of the destination folder. Never {@code null}. * The name of the destination folder. Never {@code null}.
*/ */
private void move(List<Message> messages, final String destination) { private void move(List<LocalMessage> messages, final String destination) {
copyOrMove(messages, destination, FolderOperation.MOVE); copyOrMove(messages, destination, FolderOperation.MOVE);
} }
@ -2650,12 +2651,12 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param operation * @param operation
* Specifies what operation to perform. Never {@code null}. * Specifies what operation to perform. Never {@code null}.
*/ */
private void copyOrMove(List<Message> messages, final String destination, private void copyOrMove(List<LocalMessage> messages, final String destination,
final FolderOperation operation) { final FolderOperation operation) {
Map<String, List<Message>> folderMap = new HashMap<String, List<Message>>(); Map<String, List<LocalMessage>> folderMap = new HashMap<String, List<LocalMessage>>();
for (Message message : messages) { for (LocalMessage message : messages) {
if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(message)) || if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(message)) ||
(operation == FolderOperation.COPY && !mController.isCopyCapable(message))) { (operation == FolderOperation.COPY && !mController.isCopyCapable(message))) {
@ -2674,19 +2675,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
continue; continue;
} }
List<Message> outMessages = folderMap.get(folderName); List<LocalMessage> outMessages = folderMap.get(folderName);
if (outMessages == null) { if (outMessages == null) {
outMessages = new ArrayList<Message>(); outMessages = new ArrayList<LocalMessage>();
folderMap.put(folderName, outMessages); folderMap.put(folderName, outMessages);
} }
outMessages.add(message); outMessages.add(message);
} }
for (Map.Entry<String, List<Message>> entry : folderMap.entrySet()) { for (Map.Entry<String, List<LocalMessage>> entry : folderMap.entrySet()) {
String folderName = entry.getKey(); String folderName = entry.getKey();
List<Message> outMessages = entry.getValue(); List<LocalMessage> outMessages = entry.getValue();
Account account = outMessages.get(0).getFolder().getAccount(); Account account = outMessages.get(0).getAccount();
if (operation == FolderOperation.MOVE) { if (operation == FolderOperation.MOVE) {
if (mThreadedList) { if (mThreadedList) {
@ -2859,7 +2860,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
*/ */
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.delete: { case R.id.delete: {
List<Message> messages = getCheckedMessages(); List<LocalMessage> messages = getCheckedMessages();
onDelete(messages); onDelete(messages);
mSelectedCount = 0; mSelectedCount = 0;
break; break;
@ -2887,26 +2888,22 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
// only if the account supports this // only if the account supports this
case R.id.archive: { case R.id.archive: {
List<Message> messages = getCheckedMessages(); onArchive(getCheckedMessages());
onArchive(messages);
mSelectedCount = 0; mSelectedCount = 0;
break; break;
} }
case R.id.spam: { case R.id.spam: {
List<Message> messages = getCheckedMessages(); onSpam(getCheckedMessages());
onSpam(messages);
mSelectedCount = 0; mSelectedCount = 0;
break; break;
} }
case R.id.move: { case R.id.move: {
List<Message> messages = getCheckedMessages(); onMove(getCheckedMessages());
onMove(messages);
mSelectedCount = 0; mSelectedCount = 0;
break; break;
} }
case R.id.copy: { case R.id.copy: {
List<Message> messages = getCheckedMessages(); onCopy(getCheckedMessages());
onCopy(messages);
mSelectedCount = 0; mSelectedCount = 0;
break; break;
} }
@ -2987,7 +2984,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
final Folder remoteFolder = mCurrentFolder.folder; final Folder remoteFolder = mCurrentFolder.folder;
remoteFolder.close(); remoteFolder.close();
// Send a remoteSearchFinished() message for good measure. // Send a remoteSearchFinished() message for good measure.
mListener.remoteSearchFinished(searchAccount, mCurrentFolder.name, 0, null); mListener.remoteSearchFinished(mCurrentFolder.name, 0, searchAccount.getRemoteSearchNumResults(), null);
} catch (Exception e) { } catch (Exception e) {
// Since the user is going back, log and squash any exceptions. // Since the user is going back, log and squash any exceptions.
Log.e(K9.LOG_TAG, "Could not abort remote search before going back", e); Log.e(K9.LOG_TAG, "Could not abort remote search before going back", e);
@ -3135,7 +3132,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
changeSort(mSortType); changeSort(mSortType);
} }
private Message getSelectedMessage() { private LocalMessage getSelectedMessage() {
int listViewPosition = mListView.getSelectedItemPosition(); int listViewPosition = mListView.getSelectedItemPosition();
int adapterPosition = listViewToAdapterPosition(listViewPosition); int adapterPosition = listViewToAdapterPosition(listViewPosition);
@ -3158,7 +3155,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
return AdapterView.INVALID_POSITION; return AdapterView.INVALID_POSITION;
} }
private Message getMessageAtPosition(int adapterPosition) { private LocalMessage getMessageAtPosition(int adapterPosition) {
if (adapterPosition == AdapterView.INVALID_POSITION) { if (adapterPosition == AdapterView.INVALID_POSITION) {
return null; return null;
} }
@ -3168,7 +3165,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
Account account = getAccountFromCursor(cursor); Account account = getAccountFromCursor(cursor);
long folderId = cursor.getLong(FOLDER_ID_COLUMN); long folderId = cursor.getLong(FOLDER_ID_COLUMN);
Folder folder = getFolderById(account, folderId); LocalFolder folder = getFolderById(account, folderId);
try { try {
return folder.getMessage(uid); return folder.getMessage(uid);
@ -3179,14 +3176,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
return null; return null;
} }
private List<Message> getCheckedMessages() { private List<LocalMessage> getCheckedMessages() {
List<Message> messages = new ArrayList<Message>(mSelected.size()); List<LocalMessage> messages = new ArrayList<LocalMessage>(mSelected.size());
for (int position = 0, end = mAdapter.getCount(); position < end; position++) { for (int position = 0, end = mAdapter.getCount(); position < end; position++) {
Cursor cursor = (Cursor) mAdapter.getItem(position); Cursor cursor = (Cursor) mAdapter.getItem(position);
long uniqueId = cursor.getLong(mUniqueIdColumn); long uniqueId = cursor.getLong(mUniqueIdColumn);
if (mSelected.contains(uniqueId)) { if (mSelected.contains(uniqueId)) {
Message message = getMessageAtPosition(position); LocalMessage message = getMessageAtPosition(position);
if (message != null) { if (message != null) {
messages.add(message); messages.add(message);
} }
@ -3197,7 +3194,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
} }
public void onDelete() { public void onDelete() {
Message message = getSelectedMessage(); LocalMessage message = getSelectedMessage();
if (message != null) { if (message != null) {
onDelete(Collections.singletonList(message)); onDelete(Collections.singletonList(message));
} }
@ -3227,21 +3224,21 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
} }
public void onMove() { public void onMove() {
Message message = getSelectedMessage(); LocalMessage message = getSelectedMessage();
if (message != null) { if (message != null) {
onMove(message); onMove(message);
} }
} }
public void onArchive() { public void onArchive() {
Message message = getSelectedMessage(); LocalMessage message = getSelectedMessage();
if (message != null) { if (message != null) {
onArchive(message); onArchive(message);
} }
} }
public void onCopy() { public void onCopy() {
Message message = getSelectedMessage(); LocalMessage message = getSelectedMessage();
if (message != null) { if (message != null) {
onCopy(message); onCopy(message);
} }

View File

@ -75,7 +75,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
private PgpData mPgpData; private PgpData mPgpData;
private Account mAccount; private Account mAccount;
private MessageReference mMessageReference; private MessageReference mMessageReference;
private Message mMessage; private LocalMessage mMessage;
private MessagingController mController; private MessagingController mController;
private Listener mListener = new Listener(); private Listener mListener = new Listener();
private MessageViewHandler mHandler = new MessageViewHandler(); private MessageViewHandler mHandler = new MessageViewHandler();
@ -311,7 +311,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
// Disable the delete button after it's tapped (to try to prevent // Disable the delete button after it's tapped (to try to prevent
// accidental clicks) // accidental clicks)
mFragmentListener.disableDeleteAction(); mFragmentListener.disableDeleteAction();
Message messageToDelete = mMessage; LocalMessage messageToDelete = mMessage;
mFragmentListener.showNextMessageOrReturn(); mFragmentListener.showNextMessageOrReturn();
mController.deleteMessages(Collections.singletonList(messageToDelete), null); mController.deleteMessages(Collections.singletonList(messageToDelete), null);
} }
@ -341,7 +341,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
private void refileMessage(String dstFolder) { private void refileMessage(String dstFolder) {
String srcFolder = mMessageReference.folderName; String srcFolder = mMessageReference.folderName;
Message messageToMove = mMessage; LocalMessage messageToMove = mMessage;
mFragmentListener.showNextMessageOrReturn(); mFragmentListener.showNextMessageOrReturn();
mController.moveMessage(mAccount, srcFolder, messageToMove, dstFolder, null); mController.moveMessage(mAccount, srcFolder, messageToMove, dstFolder, null);
} }
@ -576,7 +576,8 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
@Override @Override
public void loadMessageForViewBodyAvailable(final Account account, String folder, public void loadMessageForViewBodyAvailable(final Account account, String folder,
String uid, final Message message) { String uid, final Message message) {
if (!mMessageReference.uid.equals(uid) || if (!(message instanceof LocalMessage) ||
!mMessageReference.uid.equals(uid) ||
!mMessageReference.folderName.equals(folder) || !mMessageReference.folderName.equals(folder) ||
!mMessageReference.accountUuid.equals(account.getUuid())) { !mMessageReference.accountUuid.equals(account.getUuid())) {
return; return;
@ -586,7 +587,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
@Override @Override
public void run() { public void run() {
try { try {
mMessage = message; mMessage = (LocalMessage) message;
mMessageView.setMessage(account, (LocalMessage) message, mPgpData, mMessageView.setMessage(account, (LocalMessage) message, mPgpData,
mController, mListener); mController, mListener);
mFragmentListener.updateMenu(); mFragmentListener.updateMenu();

View File

@ -14,6 +14,7 @@ import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mail.store.local.LocalMessage;
public class MessageHelper { public class MessageHelper {
@ -32,8 +33,10 @@ public class MessageHelper {
mContext = context; mContext = context;
} }
public void populate(final MessageInfoHolder target, final Message message, public void populate(final MessageInfoHolder target,
final FolderInfoHolder folder, final Account account) { final LocalMessage message,
final FolderInfoHolder folder,
Account account) {
final Contacts contactHelper = K9.showContactName() ? Contacts.getInstance(mContext) : null; final Contacts contactHelper = K9.showContactName() ? Contacts.getInstance(mContext) : null;
try { try {
target.message = message; target.message = message;
@ -68,14 +71,9 @@ public class MessageHelper {
target.senderAddress = target.compareCounterparty; target.senderAddress = target.compareCounterparty;
} }
target.uid = message.getUid(); target.uid = message.getUid();
target.account = message.getFolder().getUuid();
target.account = account.getUuid(); target.uri = message.getUri();
target.uri = "email://messages/" + account.getAccountNumber() + "/" + message.getFolder().getName() + "/" + message.getUid();
} catch (MessagingException me) { } catch (MessagingException me) {
Log.w(K9.LOG_TAG, "Unable to load message info", me); Log.w(K9.LOG_TAG, "Unable to load message info", me);
} }

View File

@ -1,21 +1,17 @@
package com.fsck.k9.mail; package com.fsck.k9.mail;
import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import android.util.Log; import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.Preferences; import com.fsck.k9.Preferences;
import com.fsck.k9.controller.MessageRetrievalListener; import com.fsck.k9.controller.MessageRetrievalListener;
public abstract class Folder<T extends Message> {
public abstract class Folder {
protected final Account mAccount;
private String status = null; private String status = null;
private long lastChecked = 0; private long lastChecked = 0;
private long lastPush = 0; private long lastPush = 0;
@ -32,10 +28,6 @@ public abstract class Folder {
HOLDS_FOLDERS, HOLDS_MESSAGES, HOLDS_FOLDERS, HOLDS_MESSAGES,
} }
protected Folder(Account account) {
mAccount = account;
}
/** /**
* Forces an open of the MailProvider. If the provider is already open this * Forces an open of the MailProvider. If the provider is already open this
* function returns without doing anything. * function returns without doing anything.
@ -83,7 +75,7 @@ public abstract class Folder {
public abstract int getUnreadMessageCount() throws MessagingException; public abstract int getUnreadMessageCount() throws MessagingException;
public abstract int getFlaggedMessageCount() throws MessagingException; public abstract int getFlaggedMessageCount() throws MessagingException;
public abstract Message getMessage(String uid) throws MessagingException; public abstract T getMessage(String uid) throws MessagingException;
/** /**
* Fetch the shells of messages between a range of UIDs and after a given date. * Fetch the shells of messages between a range of UIDs and after a given date.
@ -94,7 +86,7 @@ public abstract class Folder {
* @return List of messages * @return List of messages
* @throws MessagingException * @throws MessagingException
*/ */
public abstract List<? extends Message> getMessages(int start, int end, Date earliestDate, MessageRetrievalListener listener) throws MessagingException; public abstract List<T> getMessages(int start, int end, Date earliestDate, MessageRetrievalListener<T> listener) throws MessagingException;
/** /**
* Fetches the given list of messages. The specified listener is notified as * Fetches the given list of messages. The specified listener is notified as
@ -104,13 +96,13 @@ public abstract class Folder {
* @param listener Listener to notify as we download messages. * @param listener Listener to notify as we download messages.
* @return List of messages * @return List of messages
*/ */
public abstract List<? extends Message> getMessages(MessageRetrievalListener listener) throws MessagingException; public abstract List<T> getMessages(MessageRetrievalListener<T> listener) throws MessagingException;
public List<? extends Message> getMessages(MessageRetrievalListener listener, boolean includeDeleted) throws MessagingException { public List<T> getMessages(MessageRetrievalListener<T> listener, boolean includeDeleted) throws MessagingException {
return getMessages(listener); return getMessages(listener);
} }
public abstract List<? extends Message> getMessages(String[] uids, MessageRetrievalListener listener) public abstract List<T> getMessages(String[] uids, MessageRetrievalListener<T> listener)
throws MessagingException; throws MessagingException;
public abstract Map<String, String> appendMessages(List<? extends Message> messages) throws MessagingException; public abstract Map<String, String> appendMessages(List<? extends Message> messages) throws MessagingException;
@ -149,10 +141,10 @@ public abstract class Folder {
* @throws MessagingException * @throws MessagingException
*/ */
public abstract void fetch(List<? extends Message> messages, FetchProfile fp, public abstract void fetch(List<? extends Message> messages, FetchProfile fp,
MessageRetrievalListener listener) throws MessagingException; MessageRetrievalListener<T> listener) throws MessagingException;
public void fetchPart(Message message, Part part, public void fetchPart(Message message, Part part,
MessageRetrievalListener listener) throws MessagingException { MessageRetrievalListener<T> listener) throws MessagingException {
// This is causing trouble. Disabled for now. See issue 1733 // This is causing trouble. Disabled for now. See issue 1733
//throw new RuntimeException("fetchPart() not implemented."); //throw new RuntimeException("fetchPart() not implemented.");
@ -241,10 +233,6 @@ public abstract class Folder {
this.status = status; this.status = status;
} }
public Account getAccount() {
return mAccount;
}
public List<Message> search(String queryString, final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags) public List<Message> search(String queryString, final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags)
throws MessagingException { throws MessagingException {
throw new MessagingException("K-9 does not support searches on this folder type"); throw new MessagingException("K-9 does not support searches on this folder type");

View File

@ -19,8 +19,8 @@ import com.fsck.k9.mail.store.UnavailableStorageException;
public abstract class Message implements Part, CompositeBody { public abstract class Message implements Part, CompositeBody {
protected MessageReference mReference;
private MessageReference mReference = null;
public enum RecipientType { public enum RecipientType {
TO, CC, BCC, TO, CC, BCC,
@ -55,8 +55,7 @@ public abstract class Message implements Part, CompositeBody {
} }
Message other = (Message)o; Message other = (Message)o;
return (mUid.equals(other.getUid()) return (mUid.equals(other.getUid())
&& mFolder.getName().equals(other.getFolder().getName()) && mFolder.getName().equals(other.getFolder().getName()));
&& mFolder.getAccount().getUuid().equals(other.getFolder().getAccount().getUuid()));
} }
@Override @Override
@ -65,7 +64,6 @@ public abstract class Message implements Part, CompositeBody {
int result = 1; int result = 1;
result = MULTIPLIER * result + mFolder.getName().hashCode(); result = MULTIPLIER * result + mFolder.getName().hashCode();
result = MULTIPLIER * result + mFolder.getAccount().getUuid().hashCode();
result = MULTIPLIER * result + mUid.hashCode(); result = MULTIPLIER * result + mUid.hashCode();
return result; return result;
} }
@ -75,7 +73,7 @@ public abstract class Message implements Part, CompositeBody {
} }
public void setUid(String uid) { public void setUid(String uid) {
mReference = null; this.mReference = null;
this.mUid = uid; this.mUid = uid;
} }
@ -249,14 +247,13 @@ public abstract class Message implements Part, CompositeBody {
public void destroy() throws MessagingException {} public void destroy() throws MessagingException {}
@Override @Override
public abstract void setEncoding(String encoding) throws UnavailableStorageException, MessagingException; public abstract void setEncoding(String encoding) throws MessagingException;
public abstract void setCharset(String charset) throws MessagingException; public abstract void setCharset(String charset) throws MessagingException;
public MessageReference makeMessageReference() { public MessageReference makeMessageReference() {
if (mReference == null) { if (mReference == null) {
mReference = new MessageReference(); mReference = new MessageReference();
mReference.accountUuid = getFolder().getAccount().getUuid();
mReference.folderName = getFolder().getName(); mReference.folderName = getFolder().getName();
mReference.uid = mUid; mReference.uid = mUid;
} }

View File

@ -1,24 +1,7 @@
package com.fsck.k9.mail; package com.fsck.k9.mail;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import android.app.Application;
import android.content.Context;
import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.mail.store.ImapStore;
import com.fsck.k9.mail.store.Pop3Store;
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
import com.fsck.k9.mail.store.local.LocalStore;
import com.fsck.k9.mail.store.UnavailableStorageException;
import com.fsck.k9.mail.store.WebDavStore;
/** /**
* Store is the access point for an email message store. It's location can be * Store is the access point for an email message store. It's location can be
@ -29,191 +12,6 @@ import com.fsck.k9.mail.store.WebDavStore;
* making as few network connections as possible. * making as few network connections as possible.
*/ */
public abstract class Store { public abstract class Store {
protected static final int SOCKET_CONNECT_TIMEOUT = 30000;
protected static final int SOCKET_READ_TIMEOUT = 60000;
/**
* Remote stores indexed by Uri.
*/
private static Map<String, Store> sStores = new HashMap<String, Store>();
/**
* Local stores indexed by UUID because the Uri may change due to migration to/from SD-card.
*/
private static ConcurrentMap<String, Store> sLocalStores = new ConcurrentHashMap<String, Store>();
/**
* Lock objects indexed by account UUID.
*
* @see #getLocalInstance(Account, Application)
*/
private static ConcurrentMap<String, Object> sAccountLocks = new ConcurrentHashMap<String, Object>();
/**
* Get an instance of a remote mail store.
*/
public synchronized static Store getRemoteInstance(Account account) throws MessagingException {
String uri = account.getStoreUri();
if (uri.startsWith("local")) {
throw new RuntimeException("Asked to get non-local Store object but given LocalStore URI");
}
Store store = sStores.get(uri);
if (store == null) {
if (uri.startsWith("imap")) {
store = new ImapStore(account);
} else if (uri.startsWith("pop3")) {
store = new Pop3Store(account);
} else if (uri.startsWith("webdav")) {
store = new WebDavStore(account);
}
if (store != null) {
sStores.put(uri, store);
}
}
if (store == null) {
throw new MessagingException("Unable to locate an applicable Store for " + uri);
}
return store;
}
/**
* Get an instance of a local mail store.
*
* @throws UnavailableStorageException
* if not {@link StorageProvider#isReady(Context)}
*/
public static LocalStore getLocalInstance(Account account, Application application)
throws MessagingException {
String accountUuid = account.getUuid();
// Create new per-account lock object if necessary
sAccountLocks.putIfAbsent(accountUuid, new Object());
// Get the account's lock object
Object lock = sAccountLocks.get(accountUuid);
// Use per-account locks so DatabaseUpgradeService always knows which account database is
// currently upgraded.
synchronized (lock) {
Store store = sLocalStores.get(accountUuid);
if (store == null) {
// Creating a LocalStore instance will create or upgrade the database if
// necessary. This could take some time.
store = new LocalStore(account, application);
sLocalStores.put(accountUuid, store);
}
return (LocalStore) store;
}
}
public static void removeAccount(Account account) {
try {
removeRemoteInstance(account);
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Failed to reset remote store for account " + account.getUuid(), e);
}
try {
removeLocalInstance(account);
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Failed to reset local store for account " + account.getUuid(), e);
}
}
/**
* Release reference to a local mail store instance.
*
* @param account
* {@link Account} instance that is used to get the local mail store instance.
*/
private static void removeLocalInstance(Account account) {
String accountUuid = account.getUuid();
sLocalStores.remove(accountUuid);
}
/**
* Release reference to a remote mail store instance.
*
* @param account
* {@link Account} instance that is used to get the remote mail store instance.
*/
private synchronized static void removeRemoteInstance(Account account) {
String uri = account.getStoreUri();
if (uri.startsWith("local")) {
throw new RuntimeException("Asked to get non-local Store object but given " +
"LocalStore URI");
}
sStores.remove(uri);
}
/**
* Decodes the contents of store-specific URIs and puts them into a {@link ServerSettings}
* object.
*
* @param uri
* the store-specific URI to decode
*
* @return A {@link ServerSettings} object holding the settings contained in the URI.
*
* @see ImapStore#decodeUri(String)
* @see Pop3Store#decodeUri(String)
* @see WebDavStore#decodeUri(String)
*/
public static ServerSettings decodeStoreUri(String uri) {
if (uri.startsWith("imap")) {
return ImapStore.decodeUri(uri);
} else if (uri.startsWith("pop3")) {
return Pop3Store.decodeUri(uri);
} else if (uri.startsWith("webdav")) {
return WebDavStore.decodeUri(uri);
} else {
throw new IllegalArgumentException("Not a valid store URI");
}
}
/**
* Creates a store URI from the information supplied in the {@link ServerSettings} object.
*
* @param server
* The {@link ServerSettings} object that holds the server settings.
*
* @return A store URI that holds the same information as the {@code server} parameter.
*
* @see ImapStore#createUri(ServerSettings)
* @see Pop3Store#createUri(ServerSettings)
* @see WebDavStore#createUri(ServerSettings)
*/
public static String createStoreUri(ServerSettings server) {
if (ImapStore.STORE_TYPE.equals(server.type)) {
return ImapStore.createUri(server);
} else if (Pop3Store.STORE_TYPE.equals(server.type)) {
return Pop3Store.createUri(server);
} else if (WebDavStore.STORE_TYPE.equals(server.type)) {
return WebDavStore.createUri(server);
} else {
throw new IllegalArgumentException("Not a valid store URI");
}
}
protected final Account mAccount;
protected Store(Account account) {
mAccount = account;
}
public abstract Folder getFolder(String name); public abstract Folder getFolder(String name);
public abstract List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException; public abstract List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException;
@ -244,14 +42,9 @@ public abstract class Store {
return true; return true;
} }
public void sendMessages(List<? extends Message> messages) throws MessagingException { public void sendMessages(List<? extends Message> messages) throws MessagingException { }
}
public Pusher getPusher(PushReceiver receiver) { public Pusher getPusher(PushReceiver receiver) {
return null; return null;
} }
public Account getAccount() {
return mAccount;
}
} }

View File

@ -11,7 +11,7 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
public class ImapResponseParser { class ImapResponseParser {
private static final SimpleDateFormat mDateTimeFormat = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US); private static final SimpleDateFormat mDateTimeFormat = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US);
private static final SimpleDateFormat badDateTimeFormat = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z", Locale.US); private static final SimpleDateFormat badDateTimeFormat = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z", Locale.US);
private static final SimpleDateFormat badDateTimeFormat2 = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z", Locale.US); private static final SimpleDateFormat badDateTimeFormat2 = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z", Locale.US);

View File

@ -55,7 +55,6 @@ import android.os.PowerManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.R; import com.fsck.k9.R;
import com.fsck.k9.controller.MessageRetrievalListener; import com.fsck.k9.controller.MessageRetrievalListener;
@ -78,7 +77,6 @@ import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.PushReceiver; import com.fsck.k9.mail.PushReceiver;
import com.fsck.k9.mail.Pusher; import com.fsck.k9.mail.Pusher;
import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.filter.Base64; import com.fsck.k9.mail.filter.Base64;
import com.fsck.k9.mail.filter.EOLConvertingOutputStream; import com.fsck.k9.mail.filter.EOLConvertingOutputStream;
import com.fsck.k9.mail.filter.FixedLengthInputStream; import com.fsck.k9.mail.filter.FixedLengthInputStream;
@ -105,7 +103,7 @@ import org.apache.commons.io.IOUtils;
* TODO Need a default response handler for things like folder updates * TODO Need a default response handler for things like folder updates
* </pre> * </pre>
*/ */
public class ImapStore extends Store { public class ImapStore extends RemoteStore {
public static final String STORE_TYPE = "IMAP"; public static final String STORE_TYPE = "IMAP";
private static final int IDLE_READ_TIMEOUT_INCREMENT = 5 * 60 * 1000; private static final int IDLE_READ_TIMEOUT_INCREMENT = 5 * 60 * 1000;
@ -113,7 +111,7 @@ public class ImapStore extends Store {
private static int MAX_DELAY_TIME = 5 * 60 * 1000; // 5 minutes private static int MAX_DELAY_TIME = 5 * 60 * 1000; // 5 minutes
private static int NORMAL_DELAY_TIME = 5000; private static int NORMAL_DELAY_TIME = 5000;
private static int FETCH_WINDOW_SIZE = 100; private static final int FETCH_WINDOW_SIZE = 100;
private Set<Flag> mPermanentFlagsIndex = EnumSet.noneOf(Flag.class); private Set<Flag> mPermanentFlagsIndex = EnumSet.noneOf(Flag.class);
@ -387,7 +385,7 @@ public class ImapStore extends Store {
@Override @Override
public boolean useCompression(final int type) { public boolean useCompression(final int type) {
return mAccount.useCompression(type); return mStoreConfig.useCompression(type);
} }
@Override @Override
@ -439,12 +437,12 @@ public class ImapStore extends Store {
*/ */
private final Map<String, ImapFolder> mFolderCache = new HashMap<String, ImapFolder>(); private final Map<String, ImapFolder> mFolderCache = new HashMap<String, ImapFolder>();
public ImapStore(Account account) throws MessagingException { public ImapStore(StoreConfig storeConfig) throws MessagingException {
super(account); super(storeConfig);
ImapStoreSettings settings; ImapStoreSettings settings;
try { try {
settings = decodeUri(mAccount.getStoreUri()); settings = decodeUri(storeConfig.getStoreUri());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw new MessagingException("Error while decoding store URI", e); throw new MessagingException("Error while decoding store URI", e);
} }
@ -502,7 +500,7 @@ public class ImapStore extends Store {
ImapConnection connection = getConnection(); ImapConnection connection = getConnection();
try { try {
List <? extends Folder > allFolders = listFolders(connection, false); List <? extends Folder > allFolders = listFolders(connection, false);
if (forceListAll || !mAccount.subscribedFoldersOnly()) { if (forceListAll || !mStoreConfig.subscribedFoldersOnly()) {
return allFolders; return allFolders;
} else { } else {
List<Folder> resultFolders = new LinkedList<Folder>(); List<Folder> resultFolders = new LinkedList<Folder>();
@ -570,9 +568,9 @@ public class ImapStore extends Store {
mCombinedPrefix = null; mCombinedPrefix = null;
} }
if (folder.equalsIgnoreCase(mAccount.getInboxFolderName())) { if (folder.equalsIgnoreCase(mStoreConfig.getInboxFolderName())) {
continue; continue;
} else if (folder.equals(mAccount.getOutboxFolderName())) { } else if (folder.equals(mStoreConfig.getOutboxFolderName())) {
/* /*
* There is a folder on the server with the same name as our local * There is a folder on the server with the same name as our local
* outbox. Until we have a good plan to deal with this situation * outbox. Until we have a good plan to deal with this situation
@ -604,7 +602,7 @@ public class ImapStore extends Store {
} }
} }
} }
folders.add(getFolder(mAccount.getInboxFolderName())); folders.add(getFolder(mStoreConfig.getInboxFolderName()));
return folders; return folders;
} }
@ -661,17 +659,17 @@ public class ImapStore extends Store {
for (int i = 0, count = attributes.size(); i < count; i++) { for (int i = 0, count = attributes.size(); i < count; i++) {
String attribute = attributes.getString(i); String attribute = attributes.getString(i);
if (attribute.equals("\\Drafts")) { if (attribute.equals("\\Drafts")) {
mAccount.setDraftsFolderName(decodedFolderName); mStoreConfig.setDraftsFolderName(decodedFolderName);
if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected draft folder: " + decodedFolderName); if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected draft folder: " + decodedFolderName);
} else if (attribute.equals("\\Sent")) { } else if (attribute.equals("\\Sent")) {
mAccount.setSentFolderName(decodedFolderName); mStoreConfig.setSentFolderName(decodedFolderName);
if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected sent folder: " + decodedFolderName); if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected sent folder: " + decodedFolderName);
} else if (attribute.equals("\\Spam") || attribute.equals("\\Junk")) { } else if (attribute.equals("\\Spam") || attribute.equals("\\Junk")) {
//rfc6154 just mentions \Junk //rfc6154 just mentions \Junk
mAccount.setSpamFolderName(decodedFolderName); mStoreConfig.setSpamFolderName(decodedFolderName);
if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected spam folder: " + decodedFolderName); if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected spam folder: " + decodedFolderName);
} else if (attribute.equals("\\Trash")) { } else if (attribute.equals("\\Trash")) {
mAccount.setTrashFolderName(decodedFolderName); mStoreConfig.setTrashFolderName(decodedFolderName);
if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected trash folder: " + decodedFolderName); if (K9.DEBUG) Log.d(K9.LOG_TAG, "Folder auto-configuration detected trash folder: " + decodedFolderName);
} }
} }
@ -777,7 +775,7 @@ public class ImapStore extends Store {
} }
class ImapFolder extends Folder { class ImapFolder extends Folder<ImapMessage> {
private String mName; private String mName;
protected volatile int mMessageCount = -1; protected volatile int mMessageCount = -1;
protected volatile long uidNext = -1L; protected volatile long uidNext = -1L;
@ -789,14 +787,14 @@ public class ImapStore extends Store {
private boolean mInSearch = false; private boolean mInSearch = false;
public ImapFolder(ImapStore nStore, String name) { public ImapFolder(ImapStore nStore, String name) {
super(nStore.getAccount()); super();
store = nStore; store = nStore;
this.mName = name; this.mName = name;
} }
public String getPrefixedName() throws MessagingException { public String getPrefixedName() throws MessagingException {
String prefixedName = ""; String prefixedName = "";
if (!mAccount.getInboxFolderName().equalsIgnoreCase(mName)) { if (!mStoreConfig.getInboxFolderName().equalsIgnoreCase(mName)) {
ImapConnection connection = null; ImapConnection connection = null;
synchronized (this) { synchronized (this) {
if (mConnection == null) { if (mConnection == null) {
@ -1293,7 +1291,7 @@ public class ImapStore extends Store {
} }
@Override @Override
public Message getMessage(String uid) throws MessagingException { public ImapMessage getMessage(String uid) throws MessagingException {
return new ImapMessage(uid, this); return new ImapMessage(uid, this);
} }
@ -1351,7 +1349,7 @@ public class ImapStore extends Store {
return search(searcher, listener); return search(searcher, listener);
} }
private List<Message> search(ImapSearcher searcher, MessageRetrievalListener listener) throws MessagingException { private List<Message> search(ImapSearcher searcher, MessageRetrievalListener<ImapMessage> listener) throws MessagingException {
checkOpen(); //only need READ access checkOpen(); //only need READ access
List<Message> messages = new ArrayList<Message>(); List<Message> messages = new ArrayList<Message>();
@ -1432,7 +1430,7 @@ public class ImapStore extends Store {
} }
@Override @Override
public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener listener) public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener<ImapMessage> listener)
throws MessagingException { throws MessagingException {
if (messages == null || messages.isEmpty()) { if (messages == null || messages.isEmpty()) {
return; return;
@ -1468,8 +1466,8 @@ public class ImapStore extends Store {
} }
if (fp.contains(FetchProfile.Item.BODY_SANE)) { if (fp.contains(FetchProfile.Item.BODY_SANE)) {
// If the user wants to download unlimited-size messages, don't go only for the truncated body // If the user wants to download unlimited-size messages, don't go only for the truncated body
if (mAccount.getMaximumAutoDownloadMessageSize() > 0) { if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) {
fetchFields.add(String.format(Locale.US, "BODY.PEEK[]<0.%d>", mAccount.getMaximumAutoDownloadMessageSize())); fetchFields.add(String.format(Locale.US, "BODY.PEEK[]<0.%d>", mStoreConfig.getMaximumAutoDownloadMessageSize()));
} else { } else {
fetchFields.add("BODY.PEEK[]"); fetchFields.add("BODY.PEEK[]");
} }
@ -1544,7 +1542,7 @@ public class ImapStore extends Store {
} }
if (listener != null) { if (listener != null) {
listener.messageFinished(message, messageNumber, messageMap.size()); listener.messageFinished(imapMessage, messageNumber, messageMap.size());
} }
} else { } else {
handleUntaggedResponse(response); handleUntaggedResponse(response);
@ -1572,7 +1570,7 @@ public class ImapStore extends Store {
String partId = parts[0]; String partId = parts[0];
if ("TEXT".equalsIgnoreCase(partId)) { if ("TEXT".equalsIgnoreCase(partId)) {
fetch = String.format(Locale.US, "BODY.PEEK[TEXT]<0.%d>", fetch = String.format(Locale.US, "BODY.PEEK[TEXT]<0.%d>",
mAccount.getMaximumAutoDownloadMessageSize()); mStoreConfig.getMaximumAutoDownloadMessageSize());
} else { } else {
fetch = String.format("BODY.PEEK[%s]", partId); fetch = String.format("BODY.PEEK[%s]", partId);
} }
@ -2194,7 +2192,7 @@ public class ImapStore extends Store {
} }
protected String getLogId() { protected String getLogId() {
String id = getAccount().getDescription() + ":" + getName() + "/" + Thread.currentThread().getName(); String id = mStoreConfig.toString() + ":" + getName() + "/" + Thread.currentThread().getName();
if (mConnection != null) { if (mConnection != null) {
id += "/" + mConnection.getLogId(); id += "/" + mConnection.getLogId();
} }
@ -2213,7 +2211,7 @@ public class ImapStore extends Store {
public List<Message> search(final String queryString, final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags) public List<Message> search(final String queryString, final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags)
throws MessagingException { throws MessagingException {
if (!mAccount.allowRemoteSearch()) { if (!mStoreConfig.allowRemoteSearch()) {
throw new MessagingException("Your settings do not allow remote searching of this account"); throw new MessagingException("Your settings do not allow remote searching of this account");
} }
@ -2287,7 +2285,7 @@ public class ImapStore extends Store {
} }
} }
final String encodedQry = encodeString(queryString); final String encodedQry = encodeString(queryString);
if (mAccount.isRemoteSearchFullText()) { if (mStoreConfig.isRemoteSearchFullText()) {
imapQuery += "TEXT " + encodedQry; imapQuery += "TEXT " + encodedQry;
} else { } else {
imapQuery += "OR SUBJECT " + encodedQry + " FROM " + encodedQry; imapQuery += "OR SUBJECT " + encodedQry + " FROM " + encodedQry;
@ -2424,7 +2422,7 @@ public class ImapStore extends Store {
} }
} }
setReadTimeout(Store.SOCKET_READ_TIMEOUT); setReadTimeout(SOCKET_READ_TIMEOUT);
mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(), mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(),
1024)); 1024));
@ -2458,7 +2456,7 @@ public class ImapStore extends Store {
mSocket = TrustedSocketFactory.createSocket(mSocket, mSocket = TrustedSocketFactory.createSocket(mSocket,
mSettings.getHost(), mSettings.getPort(), mSettings.getHost(), mSettings.getPort(),
mSettings.getClientCertificateAlias()); mSettings.getClientCertificateAlias());
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT); mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
mIn = new PeekableInputStream(new BufferedInputStream(mSocket mIn = new PeekableInputStream(new BufferedInputStream(mSocket
.getInputStream(), 1024)); .getInputStream(), 1024));
mParser = new ImapResponseParser(mIn); mParser = new ImapResponseParser(mIn);
@ -2953,7 +2951,7 @@ public class ImapStore extends Store {
super(store, name); super(store, name);
receiver = nReceiver; receiver = nReceiver;
TracingPowerManager pm = TracingPowerManager.getPowerManager(receiver.getContext()); TracingPowerManager pm = TracingPowerManager.getPowerManager(receiver.getContext());
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ImapFolderPusher " + store.getAccount().getDescription() + ":" + getName()); wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ImapFolderPusher " + mStoreConfig.toString() + ":" + getName());
wakeLock.setReferenceCounted(false); wakeLock.setReferenceCounted(false);
} }
@ -2968,7 +2966,7 @@ public class ImapStore extends Store {
if (doneSent.compareAndSet(false, true)) { if (doneSent.compareAndSet(false, true)) {
ImapConnection conn = mConnection; ImapConnection conn = mConnection;
if (conn != null) { if (conn != null) {
conn.setReadTimeout(Store.SOCKET_READ_TIMEOUT); conn.setReadTimeout(SOCKET_READ_TIMEOUT);
sendContinuation("DONE"); sendContinuation("DONE");
} }
@ -3030,7 +3028,7 @@ public class ImapStore extends Store {
throw new MessagingException("IMAP server is not IDLE capable:" + conn.toString()); throw new MessagingException("IMAP server is not IDLE capable:" + conn.toString());
} }
if (!stop.get() && mAccount.isPushPollOnConnect() && (conn != oldConnection || needsPoll.getAndSet(false))) { if (!stop.get() && mStoreConfig.isPushPollOnConnect() && (conn != oldConnection || needsPoll.getAndSet(false))) {
List<ImapResponse> untaggedResponses = new ArrayList<ImapResponse>(storedUntaggedResponses); List<ImapResponse> untaggedResponses = new ArrayList<ImapResponse>(storedUntaggedResponses);
storedUntaggedResponses.clear(); storedUntaggedResponses.clear();
processUntaggedResponses(untaggedResponses); processUntaggedResponses(untaggedResponses);
@ -3061,8 +3059,8 @@ public class ImapStore extends Store {
} }
} }
if (startUid < newUidNext - mAccount.getDisplayCount()) { if (startUid < newUidNext - mStoreConfig.getDisplayCount()) {
startUid = newUidNext - mAccount.getDisplayCount(); startUid = newUidNext - mStoreConfig.getDisplayCount();
} }
if (startUid < 1) { if (startUid < 1) {
startUid = 1; startUid = 1;
@ -3099,7 +3097,7 @@ public class ImapStore extends Store {
idling.set(true); idling.set(true);
doneSent.set(false); doneSent.set(false);
conn.setReadTimeout((getAccount().getIdleRefreshMinutes() * 60 * 1000) + IDLE_READ_TIMEOUT_INCREMENT); conn.setReadTimeout((mStoreConfig.getIdleRefreshMinutes() * 60 * 1000) + IDLE_READ_TIMEOUT_INCREMENT);
untaggedResponses = executeSimpleCommand(COMMAND_IDLE, false, ImapFolderPusher.this); untaggedResponses = executeSimpleCommand(COMMAND_IDLE, false, ImapFolderPusher.this);
idling.set(false); idling.set(false);
delayTime.set(NORMAL_DELAY_TIME); delayTime.set(NORMAL_DELAY_TIME);
@ -3495,7 +3493,7 @@ public class ImapStore extends Store {
@Override @Override
public int getRefreshInterval() { public int getRefreshInterval() {
return (getAccount().getIdleRefreshMinutes() * 60 * 1000); return (mStoreConfig.getIdleRefreshMinutes() * 60 * 1000);
} }
@Override @Override

View File

@ -3,7 +3,6 @@ package com.fsck.k9.mail.store;
import android.util.Log; import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.R; import com.fsck.k9.R;
import com.fsck.k9.controller.MessageRetrievalListener; import com.fsck.k9.controller.MessageRetrievalListener;
@ -24,7 +23,6 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.LinkedList; import java.util.LinkedList;
@ -35,7 +33,7 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
public class Pop3Store extends Store { public class Pop3Store extends RemoteStore {
public static final String STORE_TYPE = "POP3"; public static final String STORE_TYPE = "POP3";
private static final String STLS_COMMAND = "STLS"; private static final String STLS_COMMAND = "STLS";
@ -209,12 +207,12 @@ public class Pop3Store extends Store {
private boolean mTopNotSupported; private boolean mTopNotSupported;
public Pop3Store(Account account) throws MessagingException { public Pop3Store(StoreConfig storeConfig) throws MessagingException {
super(account); super(storeConfig);
ServerSettings settings; ServerSettings settings;
try { try {
settings = decodeUri(mAccount.getStoreUri()); settings = decodeUri(storeConfig.getStoreUri());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw new MessagingException("Error while decoding store URI", e); throw new MessagingException("Error while decoding store URI", e);
} }
@ -243,13 +241,13 @@ public class Pop3Store extends Store {
@Override @Override
public List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException { public List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException {
List<Folder> folders = new LinkedList<Folder>(); List<Folder> folders = new LinkedList<Folder>();
folders.add(getFolder(mAccount.getInboxFolderName())); folders.add(getFolder(mStoreConfig.getInboxFolderName()));
return folders; return folders;
} }
@Override @Override
public void checkSettings() throws MessagingException { public void checkSettings() throws MessagingException {
Pop3Folder folder = new Pop3Folder(mAccount.getInboxFolderName()); Pop3Folder folder = new Pop3Folder(mStoreConfig.getInboxFolderName());
folder.open(Folder.OPEN_MODE_RW); folder.open(Folder.OPEN_MODE_RW);
if (!mCapabilities.uidl) { if (!mCapabilities.uidl) {
/* /*
@ -272,7 +270,7 @@ public class Pop3Store extends Store {
return false; return false;
} }
class Pop3Folder extends Folder { class Pop3Folder extends Folder<Pop3Message> {
private Socket mSocket; private Socket mSocket;
private InputStream mIn; private InputStream mIn;
private OutputStream mOut; private OutputStream mOut;
@ -283,11 +281,11 @@ public class Pop3Store extends Store {
private int mMessageCount; private int mMessageCount;
public Pop3Folder(String name) { public Pop3Folder(String name) {
super(Pop3Store.this.mAccount); super();
this.mName = name; this.mName = name;
if (mName.equalsIgnoreCase(mAccount.getInboxFolderName())) { if (mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName())) {
mName = mAccount.getInboxFolderName(); mName = mStoreConfig.getInboxFolderName();
} }
} }
@ -297,7 +295,7 @@ public class Pop3Store extends Store {
return; return;
} }
if (!mName.equalsIgnoreCase(mAccount.getInboxFolderName())) { if (!mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName())) {
throw new MessagingException("Folder does not exist"); throw new MessagingException("Folder does not exist");
} }
@ -313,7 +311,7 @@ public class Pop3Store extends Store {
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024); mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512); mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT); mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
if (!isOpen()) { if (!isOpen()) {
throw new MessagingException("Unable to connect socket"); throw new MessagingException("Unable to connect socket");
} }
@ -328,7 +326,7 @@ public class Pop3Store extends Store {
mSocket = TrustedSocketFactory.createSocket(mSocket, mHost, mPort, mSocket = TrustedSocketFactory.createSocket(mSocket, mHost, mPort,
mClientCertificateAlias); mClientCertificateAlias);
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT); mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024); mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512); mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
if (!isOpen()) { if (!isOpen()) {
@ -541,7 +539,7 @@ public class Pop3Store extends Store {
@Override @Override
public boolean exists() throws MessagingException { public boolean exists() throws MessagingException {
return mName.equalsIgnoreCase(mAccount.getInboxFolderName()); return mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName());
} }
@Override @Override
@ -559,7 +557,7 @@ public class Pop3Store extends Store {
} }
@Override @Override
public Message getMessage(String uid) throws MessagingException { public Pop3Message getMessage(String uid) throws MessagingException {
Pop3Message message = mUidToMsgMap.get(uid); Pop3Message message = mUidToMsgMap.get(uid);
if (message == null) { if (message == null) {
message = new Pop3Message(uid, this); message = new Pop3Message(uid, this);
@ -761,7 +759,7 @@ public class Pop3Store extends Store {
* @throws MessagingException * @throws MessagingException
*/ */
@Override @Override
public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener listener) public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener<Pop3Message> listener)
throws MessagingException { throws MessagingException {
if (messages == null || messages.isEmpty()) { if (messages == null || messages.isEmpty()) {
return; return;
@ -805,9 +803,9 @@ public class Pop3Store extends Store {
* To convert the suggested download size we take the size * To convert the suggested download size we take the size
* divided by the maximum line size (76). * divided by the maximum line size (76).
*/ */
if (mAccount.getMaximumAutoDownloadMessageSize() > 0) { if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) {
fetchBody(pop3Message, fetchBody(pop3Message,
(mAccount.getMaximumAutoDownloadMessageSize() / 76)); (mStoreConfig.getMaximumAutoDownloadMessageSize() / 76));
} else { } else {
fetchBody(pop3Message, -1); fetchBody(pop3Message, -1);
} }
@ -819,7 +817,7 @@ public class Pop3Store extends Store {
pop3Message.setBody(null); pop3Message.setBody(null);
} }
if (listener != null && !(fp.contains(FetchProfile.Item.ENVELOPE) && fp.size() == 1)) { if (listener != null && !(fp.contains(FetchProfile.Item.ENVELOPE) && fp.size() == 1)) {
listener.messageFinished(message, i, count); listener.messageFinished(pop3Message, i, count);
} }
} catch (IOException ioe) { } catch (IOException ioe) {
throw new MessagingException("Unable to fetch message", ioe); throw new MessagingException("Unable to fetch message", ioe);

View File

@ -0,0 +1,122 @@
package com.fsck.k9.mail.store;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Store;
import java.util.HashMap;
import java.util.Map;
public abstract class RemoteStore extends Store {
protected static final int SOCKET_CONNECT_TIMEOUT = 30000;
protected static final int SOCKET_READ_TIMEOUT = 60000;
protected StoreConfig mStoreConfig;
/**
* Remote stores indexed by Uri.
*/
private static Map<String, Store> sStores = new HashMap<String, Store>();
public RemoteStore(StoreConfig storeConfig) {
mStoreConfig = storeConfig;
}
/**
* Get an instance of a remote mail store.
*/
public synchronized static Store getInstance(StoreConfig storeConfig) throws MessagingException {
String uri = storeConfig.getStoreUri();
if (uri.startsWith("local")) {
throw new RuntimeException("Asked to get non-local Store object but given LocalStore URI");
}
Store store = sStores.get(uri);
if (store == null) {
if (uri.startsWith("imap")) {
store = new ImapStore(storeConfig);
} else if (uri.startsWith("pop3")) {
store = new Pop3Store(storeConfig);
} else if (uri.startsWith("webdav")) {
store = new WebDavStore(storeConfig);
}
if (store != null) {
sStores.put(uri, store);
}
}
if (store == null) {
throw new MessagingException("Unable to locate an applicable Store for " + uri);
}
return store;
}
/**
* Release reference to a remote mail store instance.
*
* @param storeConfig {@link com.fsck.k9.mail.store.StoreConfig} instance that is used to get the remote mail store instance.
*/
public static void removeInstance(StoreConfig storeConfig) {
String uri = storeConfig.getStoreUri();
if (uri.startsWith("local")) {
throw new RuntimeException("Asked to get non-local Store object but given " +
"LocalStore URI");
}
sStores.remove(uri);
}
/**
* Decodes the contents of store-specific URIs and puts them into a {@link com.fsck.k9.mail.ServerSettings}
* object.
*
* @param uri
* the store-specific URI to decode
*
* @return A {@link com.fsck.k9.mail.ServerSettings} object holding the settings contained in the URI.
*
* @see com.fsck.k9.mail.store.ImapStore#decodeUri(String)
* @see com.fsck.k9.mail.store.Pop3Store#decodeUri(String)
* @see com.fsck.k9.mail.store.WebDavStore#decodeUri(String)
*/
public static ServerSettings decodeStoreUri(String uri) {
if (uri.startsWith("imap")) {
return ImapStore.decodeUri(uri);
} else if (uri.startsWith("pop3")) {
return Pop3Store.decodeUri(uri);
} else if (uri.startsWith("webdav")) {
return WebDavStore.decodeUri(uri);
} else {
throw new IllegalArgumentException("Not a valid store URI");
}
}
/**
* Creates a store URI from the information supplied in the {@link com.fsck.k9.mail.ServerSettings} object.
*
* @param server
* The {@link com.fsck.k9.mail.ServerSettings} object that holds the server settings.
*
* @return A store URI that holds the same information as the {@code server} parameter.
*
* @see com.fsck.k9.mail.store.ImapStore#createUri(com.fsck.k9.mail.ServerSettings)
* @see com.fsck.k9.mail.store.Pop3Store#createUri(com.fsck.k9.mail.ServerSettings)
* @see com.fsck.k9.mail.store.WebDavStore#createUri(com.fsck.k9.mail.ServerSettings)
*/
public static String createStoreUri(ServerSettings server) {
if (ImapStore.STORE_TYPE.equals(server.type)) {
return ImapStore.createUri(server);
} else if (Pop3Store.STORE_TYPE.equals(server.type)) {
return Pop3Store.createUri(server);
} else if (WebDavStore.STORE_TYPE.equals(server.type)) {
return WebDavStore.createUri(server);
} else {
throw new IllegalArgumentException("Not a valid store URI");
}
}
}

View File

@ -0,0 +1,32 @@
package com.fsck.k9.mail.store;
public interface StoreConfig {
String getUuid();
String getStoreUri();
String getTransportUri();
boolean subscribedFoldersOnly();
boolean useCompression(int type);
String getInboxFolderName();
String getOutboxFolderName();
String getDraftsFolderName();
void setInboxFolderName(String folderName);
void setDraftsFolderName(String decodedFolderName);
void setTrashFolderName(String decodedFolderName);
void setSpamFolderName(String decodedFolderName);
void setSentFolderName(String decodedFolderName);
void setAutoExpandFolderName(String folderName);
int getMaximumAutoDownloadMessageSize();
boolean allowRemoteSearch();
boolean isRemoteSearchFullText();
boolean isPushPollOnConnect();
int getDisplayCount();
int getIdleRefreshMinutes();
}

View File

@ -2,7 +2,6 @@ package com.fsck.k9.mail.store;
import android.util.Log; import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.controller.MessageRetrievalListener; import com.fsck.k9.controller.MessageRetrievalListener;
@ -55,7 +54,7 @@ import java.util.zip.GZIPInputStream;
* and email information. * and email information.
* </pre> * </pre>
*/ */
public class WebDavStore extends Store { public class WebDavStore extends RemoteStore {
public static final String STORE_TYPE = "WebDAV"; public static final String STORE_TYPE = "WebDAV";
// Authentication types // Authentication types
@ -294,12 +293,12 @@ public class WebDavStore extends Store {
private Map<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>(); private Map<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
public WebDavStore(Account account) throws MessagingException { public WebDavStore(StoreConfig storeConfig) throws MessagingException {
super(account); super(storeConfig);
WebDavStoreSettings settings; WebDavStoreSettings settings;
try { try {
settings = decodeUri(mAccount.getStoreUri()); settings = decodeUri(storeConfig.getStoreUri());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw new MessagingException("Error while decoding store URI", e); throw new MessagingException("Error while decoding store URI", e);
} }
@ -380,21 +379,21 @@ public class WebDavStore extends Store {
Map<String, String> specialFoldersMap = dataset.getSpecialFolderToUrl(); Map<String, String> specialFoldersMap = dataset.getSpecialFolderToUrl();
String folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_INBOX_FOLDER)); String folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_INBOX_FOLDER));
if (folderName != null) { if (folderName != null) {
mAccount.setAutoExpandFolderName(folderName); mStoreConfig.setAutoExpandFolderName(folderName);
mAccount.setInboxFolderName(folderName); mStoreConfig.setInboxFolderName(folderName);
} }
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_DRAFTS_FOLDER)); folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_DRAFTS_FOLDER));
if (folderName != null) if (folderName != null)
mAccount.setDraftsFolderName(folderName); mStoreConfig.setDraftsFolderName(folderName);
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_TRASH_FOLDER)); folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_TRASH_FOLDER));
if (folderName != null) if (folderName != null)
mAccount.setTrashFolderName(folderName); mStoreConfig.setTrashFolderName(folderName);
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SPAM_FOLDER)); folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SPAM_FOLDER));
if (folderName != null) if (folderName != null)
mAccount.setSpamFolderName(folderName); mStoreConfig.setSpamFolderName(folderName);
// K-9 Mail's outbox is a special local folder and different from Exchange/WebDAV's outbox. // K-9 Mail's outbox is a special local folder and different from Exchange/WebDAV's outbox.
/* /*
@ -405,7 +404,7 @@ public class WebDavStore extends Store {
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SENT_FOLDER)); folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SENT_FOLDER));
if (folderName != null) if (folderName != null)
mAccount.setSentFolderName(folderName); mStoreConfig.setSentFolderName(folderName);
/** /**
* Next we get all the folders (including "special" ones) * Next we get all the folders (including "special" ones)
@ -1196,7 +1195,7 @@ public class WebDavStore extends Store {
@Override @Override
public void sendMessages(List<? extends Message> messages) throws MessagingException { public void sendMessages(List<? extends Message> messages) throws MessagingException {
WebDavFolder tmpFolder = (WebDavStore.WebDavFolder) getFolder(mAccount.getDraftsFolderName()); WebDavFolder tmpFolder = (WebDavStore.WebDavFolder) getFolder(mStoreConfig.getDraftsFolderName());
try { try {
tmpFolder.open(Folder.OPEN_MODE_RW); tmpFolder.open(Folder.OPEN_MODE_RW);
List<? extends Message> retMessages = tmpFolder.appendWebDavMessages(messages); List<? extends Message> retMessages = tmpFolder.appendWebDavMessages(messages);
@ -1216,7 +1215,7 @@ public class WebDavStore extends Store {
/** /**
* A WebDav Folder * A WebDav Folder
*/ */
class WebDavFolder extends Folder { class WebDavFolder extends Folder<WebDavMessage> {
private String mName; private String mName;
private String mFolderUrl; private String mFolderUrl;
private boolean mIsOpen = false; private boolean mIsOpen = false;
@ -1229,7 +1228,7 @@ public class WebDavStore extends Store {
} }
public WebDavFolder(WebDavStore nStore, String name) { public WebDavFolder(WebDavStore nStore, String name) {
super(nStore.getAccount()); super();
store = nStore; store = nStore;
this.mName = name; this.mName = name;
@ -1397,7 +1396,7 @@ public class WebDavStore extends Store {
} }
@Override @Override
public Message getMessage(String uid) throws MessagingException { public WebDavMessage getMessage(String uid) throws MessagingException {
return new WebDavMessage(uid, this); return new WebDavMessage(uid, this);
} }
@ -1498,7 +1497,7 @@ public class WebDavStore extends Store {
} }
@Override @Override
public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener listener) public void fetch(List<? extends Message> messages, FetchProfile fp, MessageRetrievalListener<WebDavMessage> listener)
throws MessagingException { throws MessagingException {
if (messages == null || if (messages == null ||
messages.isEmpty()) { messages.isEmpty()) {
@ -1519,8 +1518,8 @@ public class WebDavStore extends Store {
} }
if (fp.contains(FetchProfile.Item.BODY_SANE)) { if (fp.contains(FetchProfile.Item.BODY_SANE)) {
if (mAccount.getMaximumAutoDownloadMessageSize() > 0) { if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) {
fetchMessages(messages, listener, (mAccount.getMaximumAutoDownloadMessageSize() / 76)); fetchMessages(messages, listener, (mStoreConfig.getMaximumAutoDownloadMessageSize() / 76));
} else { } else {
fetchMessages(messages, listener, -1); fetchMessages(messages, listener, -1);
} }
@ -1533,7 +1532,7 @@ public class WebDavStore extends Store {
/** /**
* Fetches the full messages or up to lines lines and passes them to the message parser. * Fetches the full messages or up to lines lines and passes them to the message parser.
*/ */
private void fetchMessages(List<? extends Message> messages, MessageRetrievalListener listener, int lines) private void fetchMessages(List<? extends Message> messages, MessageRetrievalListener<WebDavMessage> listener, int lines)
throws MessagingException { throws MessagingException {
WebDavHttpClient httpclient; WebDavHttpClient httpclient;
httpclient = getHttpClient(); httpclient = getHttpClient();
@ -1594,8 +1593,8 @@ public class WebDavStore extends Store {
if (entity != null) { if (entity != null) {
InputStream istream = null; InputStream istream = null;
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
String tempText = ""; String tempText;
String resultText = ""; String resultText;
BufferedReader reader = null; BufferedReader reader = null;
int currentLines = 0; int currentLines = 0;

View File

@ -28,6 +28,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.Account.MessageFormat; import com.fsck.k9.Account.MessageFormat;
import com.fsck.k9.activity.Search; import com.fsck.k9.activity.Search;
@ -58,7 +59,7 @@ import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback;
import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException; import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException;
import com.fsck.k9.provider.AttachmentProvider; import com.fsck.k9.provider.AttachmentProvider;
public class LocalFolder extends Folder implements Serializable { public class LocalFolder extends Folder<LocalMessage> implements Serializable {
private static final long serialVersionUID = -1973296520918624767L; private static final long serialVersionUID = -1973296520918624767L;
@ -80,22 +81,19 @@ public class LocalFolder extends Folder implements Serializable {
private Integer mLastUid = null; private Integer mLastUid = null;
public LocalFolder(LocalStore localStore, String name) { public LocalFolder(LocalStore localStore, String name) {
super(localStore.getAccount()); super();
this.localStore = localStore; this.localStore = localStore;
this.mName = name; this.mName = name;
if (this.localStore.getAccount().getInboxFolderName().equals(getName())) { if (getAccount().getInboxFolderName().equals(getName())) {
mSyncClass = FolderClass.FIRST_CLASS; mSyncClass = FolderClass.FIRST_CLASS;
mPushClass = FolderClass.FIRST_CLASS; mPushClass = FolderClass.FIRST_CLASS;
mInTopGroup = true; mInTopGroup = true;
} }
} }
public LocalFolder(LocalStore localStore, long id) { public LocalFolder(LocalStore localStore, long id) {
super(localStore.getAccount()); super();
this.localStore = localStore; this.localStore = localStore;
this.mFolderId = id; this.mFolderId = id;
} }
@ -104,6 +102,25 @@ public class LocalFolder extends Folder implements Serializable {
return mFolderId; return mFolderId;
} }
public String getUuid()
{
return getAccount().getUuid();
}
public boolean getSignatureUse() {
return getAccount().getSignatureUse();
}
public void setLastSelectedFolderName(String destFolderName) {
getAccount().setLastSelectedFolderName(destFolderName);
}
public boolean syncRemoteDeletions() {
return getAccount().syncRemoteDeletions();
}
@Override @Override
public void open(final int mode) throws MessagingException { public void open(final int mode) throws MessagingException {
@ -216,7 +233,7 @@ public class LocalFolder extends Folder implements Serializable {
@Override @Override
public boolean create(FolderType type) throws MessagingException { public boolean create(FolderType type) throws MessagingException {
return create(type, mAccount.getDisplayCount()); return create(type, getAccount().getDisplayCount());
} }
@Override @Override
@ -517,25 +534,25 @@ public class LocalFolder extends Folder implements Serializable {
String id = getPrefId(); String id = getPrefId();
// there can be a lot of folders. For the defaults, let's not save prefs, saving space, except for INBOX // there can be a lot of folders. For the defaults, let's not save prefs, saving space, except for INBOX
if (mDisplayClass == FolderClass.NO_CLASS && !mAccount.getInboxFolderName().equals(getName())) { if (mDisplayClass == FolderClass.NO_CLASS && !getAccount().getInboxFolderName().equals(getName())) {
editor.remove(id + ".displayMode"); editor.remove(id + ".displayMode");
} else { } else {
editor.putString(id + ".displayMode", mDisplayClass.name()); editor.putString(id + ".displayMode", mDisplayClass.name());
} }
if (mSyncClass == FolderClass.INHERITED && !mAccount.getInboxFolderName().equals(getName())) { if (mSyncClass == FolderClass.INHERITED && !getAccount().getInboxFolderName().equals(getName())) {
editor.remove(id + ".syncMode"); editor.remove(id + ".syncMode");
} else { } else {
editor.putString(id + ".syncMode", mSyncClass.name()); editor.putString(id + ".syncMode", mSyncClass.name());
} }
if (mNotifyClass == FolderClass.INHERITED && !mAccount.getInboxFolderName().equals(getName())) { if (mNotifyClass == FolderClass.INHERITED && !getAccount().getInboxFolderName().equals(getName())) {
editor.remove(id + ".notifyMode"); editor.remove(id + ".notifyMode");
} else { } else {
editor.putString(id + ".notifyMode", mNotifyClass.name()); editor.putString(id + ".notifyMode", mNotifyClass.name());
} }
if (mPushClass == FolderClass.SECOND_CLASS && !mAccount.getInboxFolderName().equals(getName())) { if (mPushClass == FolderClass.SECOND_CLASS && !getAccount().getInboxFolderName().equals(getName())) {
editor.remove(id + ".pushMode"); editor.remove(id + ".pushMode");
} else { } else {
editor.putString(id + ".pushMode", mPushClass.name()); editor.putString(id + ".pushMode", mPushClass.name());
@ -597,7 +614,7 @@ public class LocalFolder extends Folder implements Serializable {
} }
@Override @Override
public void fetch(final List<? extends Message> messages, final FetchProfile fp, final MessageRetrievalListener listener) public void fetch(final List<? extends Message> messages, final FetchProfile fp, final MessageRetrievalListener<LocalMessage> listener)
throws MessagingException { throws MessagingException {
try { try {
this.localStore.database.execute(false, new DbCallback<Void>() { this.localStore.database.execute(false, new DbCallback<Void>() {
@ -614,7 +631,7 @@ public class LocalFolder extends Folder implements Serializable {
try { try {
cursor = db.rawQuery("SELECT html_content, text_content, mime_type FROM messages " cursor = db.rawQuery("SELECT html_content, text_content, mime_type FROM messages "
+ "WHERE id = ?", + "WHERE id = ?",
new String[] { Long.toString(localMessage.mId) }); new String[] { Long.toString(localMessage.getId()) });
cursor.moveToNext(); cursor.moveToNext();
String htmlContent = cursor.getString(0); String htmlContent = cursor.getString(0);
String textContent = cursor.getString(1); String textContent = cursor.getString(1);
@ -629,7 +646,7 @@ public class LocalFolder extends Folder implements Serializable {
mp.addBodyPart(bp); mp.addBodyPart(bp);
} }
if (mAccount.getMessageFormat() != MessageFormat.TEXT) { if (getAccount().getMessageFormat() != MessageFormat.TEXT) {
if (htmlContent != null) { if (htmlContent != null) {
TextBody body = new TextBody(htmlContent); TextBody body = new TextBody(htmlContent);
MimeBodyPart bp = new MimeBodyPart(body, "text/html"); MimeBodyPart bp = new MimeBodyPart(body, "text/html");
@ -700,7 +717,7 @@ public class LocalFolder extends Folder implements Serializable {
"content_disposition" "content_disposition"
}, },
"message_id = ?", "message_id = ?",
new String[] { Long.toString(localMessage.mId) }, new String[] { Long.toString(localMessage.getId()) },
null, null,
null, null,
null); null);
@ -1439,12 +1456,12 @@ public class LocalFolder extends Folder implements Serializable {
message.isSet(Flag.FLAGGED) ? 1 : 0, message.isSet(Flag.FLAGGED) ? 1 : 0,
message.isSet(Flag.ANSWERED) ? 1 : 0, message.isSet(Flag.ANSWERED) ? 1 : 0,
message.isSet(Flag.FORWARDED) ? 1 : 0, message.isSet(Flag.FORWARDED) ? 1 : 0,
message.mId message.getId()
}); });
for (int i = 0, count = attachments.size(); i < count; i++) { for (int i = 0, count = attachments.size(); i < count; i++) {
Part attachment = attachments.get(i); Part attachment = attachments.get(i);
saveAttachment(message.mId, attachment, false); saveAttachment(message.getId(), attachment, false);
} }
saveHeaders(message.getId(), message); saveHeaders(message.getId(), message);
} catch (Exception e) { } catch (Exception e) {
@ -1637,7 +1654,7 @@ public class LocalFolder extends Folder implements Serializable {
File attachmentFile = new File(attachmentDirectory, Long.toString(attachmentId)); File attachmentFile = new File(attachmentDirectory, Long.toString(attachmentId));
tempAttachmentFile.renameTo(attachmentFile); tempAttachmentFile.renameTo(attachmentFile);
contentUri = AttachmentProvider.getAttachmentUri( contentUri = AttachmentProvider.getAttachmentUri(
mAccount, getAccount(),
attachmentId); attachmentId);
if (MimeUtil.isMessage(attachment.getMimeType())) { if (MimeUtil.isMessage(attachment.getMimeType())) {
attachment.setBody(new LocalAttachmentMessageBody( attachment.setBody(new LocalAttachmentMessageBody(
@ -1712,7 +1729,7 @@ public class LocalFolder extends Folder implements Serializable {
@Override @Override
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
db.update("messages", cv, "id = ?", new String[] db.update("messages", cv, "id = ?", new String[]
{ Long.toString(message.mId) }); { Long.toString(message.getId()) });
return null; return null;
} }
}); });
@ -1829,7 +1846,7 @@ public class LocalFolder extends Folder implements Serializable {
setPushState(null); setPushState(null);
setLastPush(0); setLastPush(0);
setLastChecked(0); setLastChecked(0);
setVisibleLimit(mAccount.getDisplayCount()); setVisibleLimit(getAccount().getDisplayCount());
} }
@Override @Override
@ -1878,7 +1895,7 @@ public class LocalFolder extends Folder implements Serializable {
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException { public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
Cursor attachmentsCursor = null; Cursor attachmentsCursor = null;
try { try {
String accountUuid = mAccount.getUuid(); String accountUuid = getUuid();
Context context = LocalFolder.this.localStore.mApplication; Context context = LocalFolder.this.localStore.mApplication;
// Get attachment IDs // Get attachment IDs
@ -2039,7 +2056,7 @@ public class LocalFolder extends Folder implements Serializable {
// Append the first message ID from the "In-Reply-To" header line // Append the first message ID from the "In-Reply-To" header line
String[] inReplyToArray = message.getHeader("In-Reply-To"); String[] inReplyToArray = message.getHeader("In-Reply-To");
String inReplyTo = null; String inReplyTo;
if (inReplyToArray != null && inReplyToArray.length > 0) { if (inReplyToArray != null && inReplyToArray.length > 0) {
inReplyTo = Utility.extractMessageId(inReplyToArray[0]); inReplyTo = Utility.extractMessageId(inReplyToArray[0]);
if (inReplyTo != null) { if (inReplyTo != null) {
@ -2194,4 +2211,8 @@ public class LocalFolder extends Folder implements Serializable {
throw(MessagingException) e.getCause(); throw(MessagingException) e.getCause();
} }
} }
private Account getAccount() {
return localStore.getAccount();
}
} }

View File

@ -12,7 +12,9 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.util.Log; import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.activity.MessageReference;
import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Folder;
@ -28,7 +30,7 @@ public class LocalMessage extends MimeMessage {
private final LocalStore localStore; private final LocalStore localStore;
long mId; private long mId;
private int mAttachmentCount; private int mAttachmentCount;
private String mSubject; private String mSubject;
@ -557,4 +559,43 @@ public class LocalMessage extends MimeMessage {
public long getRootId() { public long getRootId() {
return mRootId; return mRootId;
} }
public Account getAccount() {
return localStore.getAccount();
}
@Override
public MessageReference makeMessageReference() {
if (mReference == null) {
mReference = super.makeMessageReference();
mReference.accountUuid = getFolder().getUuid();
}
return mReference;
}
@Override
public LocalFolder getFolder() {
return (LocalFolder) super.getFolder();
}
public String getUri() {
return "email://messages/" + getAccount().getAccountNumber() + "/" + getFolder().getName() + "/" + getUid();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
LocalMessage that = (LocalMessage) o;
return !(getUid() != null ? !getUid().equals(that.getUid()) : that.getUid() != null);
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (getUid() != null ? getUid().hashCode() : 0);
return result;
}
} }

View File

@ -10,6 +10,8 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import android.app.Application; import android.app.Application;
import android.content.ContentResolver; import android.content.ContentResolver;
@ -32,6 +34,8 @@ import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Store; import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.store.RemoteStore;
import com.fsck.k9.mail.store.StoreConfig;
import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback; import com.fsck.k9.mail.store.local.LockableDatabase.DbCallback;
import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException; import com.fsck.k9.mail.store.local.LockableDatabase.WrappedException;
import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.mail.store.StorageManager;
@ -56,6 +60,18 @@ public class LocalStore extends Store implements Serializable {
static final String[] EMPTY_STRING_ARRAY = new String[0]; static final String[] EMPTY_STRING_ARRAY = new String[0];
static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
/**
* Lock objects indexed by account UUID.
*
* @see #getInstance(Account, Application)
*/
private static ConcurrentMap<String, Object> sAccountLocks = new ConcurrentHashMap<String, Object>();
/**
* Local stores indexed by UUID because the Uri may change due to migration to/from SD-card.
*/
private static ConcurrentMap<String, Store> sLocalStores = new ConcurrentHashMap<String, Store>();
/* /*
* a String containing the columns getMessages expects to work with * a String containing the columns getMessages expects to work with
* in the correct order. * in the correct order.
@ -138,6 +154,7 @@ public class LocalStore extends Store implements Serializable {
LockableDatabase database; LockableDatabase database;
private ContentResolver mContentResolver; private ContentResolver mContentResolver;
private final Account mAccount;
/** /**
* local://localhost/path/to/database/uuid.db * local://localhost/path/to/database/uuid.db
@ -147,7 +164,7 @@ public class LocalStore extends Store implements Serializable {
* @throws UnavailableStorageException if not {@link StorageProvider#isReady(Context)} * @throws UnavailableStorageException if not {@link StorageProvider#isReady(Context)}
*/ */
public LocalStore(final Account account, final Application application) throws MessagingException { public LocalStore(final Account account, final Application application) throws MessagingException {
super(account); mAccount = account;
database = new LockableDatabase(application, account.getUuid(), new StoreSchemaDefinition(this)); database = new LockableDatabase(application, account.getUuid(), new StoreSchemaDefinition(this));
mApplication = application; mApplication = application;
@ -158,10 +175,73 @@ public class LocalStore extends Store implements Serializable {
database.open(); database.open();
} }
/**
* Get an instance of a local mail store.
*
* @throws UnavailableStorageException
* if not {@link StorageProvider#isReady(Context)}
*/
public static LocalStore getInstance(Account account, Application application)
throws MessagingException {
String accountUuid = account.getUuid();
// Create new per-account lock object if necessary
sAccountLocks.putIfAbsent(accountUuid, new Object());
// Get the account's lock object
Object lock = sAccountLocks.get(accountUuid);
// Use per-account locks so DatabaseUpgradeService always knows which account database is
// currently upgraded.
synchronized (lock) {
Store store = sLocalStores.get(accountUuid);
if (store == null) {
// Creating a LocalStore instance will create or upgrade the database if
// necessary. This could take some time.
store = new LocalStore(account, application);
sLocalStores.put(accountUuid, store);
}
return (LocalStore) store;
}
}
public static void removeAccount(StoreConfig storeConfig) {
try {
RemoteStore.removeInstance(storeConfig);
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Failed to reset remote store for account " + storeConfig.getUuid(), e);
}
try {
removeInstance(storeConfig);
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Failed to reset local store for account " + storeConfig.getUuid(), e);
}
}
/**
* Release reference to a local mail store instance.
*
* @param account
* {@link Account} instance that is used to get the local mail store instance.
*/
private static void removeInstance(StoreConfig account) {
String accountUuid = account.getUuid();
sLocalStores.remove(accountUuid);
}
public void switchLocalStorage(final String newStorageProviderId) throws MessagingException { public void switchLocalStorage(final String newStorageProviderId) throws MessagingException {
database.switchProvider(newStorageProviderId); database.switchProvider(newStorageProviderId);
} }
protected Account getAccount() {
return mAccount;
}
protected SharedPreferences getPreferences() { protected SharedPreferences getPreferences() {
return Preferences.getPreferences(mApplication).getPreferences(); return Preferences.getPreferences(mApplication).getPreferences();
} }

View File

@ -15,6 +15,7 @@ import com.fsck.k9.mail.filter.LineWrapOutputStream;
import com.fsck.k9.mail.filter.PeekableInputStream; import com.fsck.k9.mail.filter.PeekableInputStream;
import com.fsck.k9.mail.filter.SmtpDataStuffing; import com.fsck.k9.mail.filter.SmtpDataStuffing;
import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.store.StoreConfig;
import com.fsck.k9.mail.store.local.LocalMessage; import com.fsck.k9.mail.store.local.LocalMessage;
import com.fsck.k9.net.ssl.TrustedSocketFactory; import com.fsck.k9.net.ssl.TrustedSocketFactory;
@ -183,7 +184,7 @@ public class SmtpTransport extends Transport {
private boolean m8bitEncodingAllowed; private boolean m8bitEncodingAllowed;
private int mLargestAcceptableMessage; private int mLargestAcceptableMessage;
public SmtpTransport(Account account) throws MessagingException { public SmtpTransport(StoreConfig account) throws MessagingException {
ServerSettings settings; ServerSettings settings;
try { try {
settings = decodeUri(account.getTransportUri()); settings = decodeUri(account.getTransportUri());

View File

@ -3,12 +3,12 @@ package com.fsck.k9.mail.transport;
import android.util.Log; import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.store.StoreConfig;
import com.fsck.k9.mail.store.WebDavStore; import com.fsck.k9.mail.store.WebDavStore;
import java.util.Collections; import java.util.Collections;
@ -43,12 +43,8 @@ public class WebDavTransport extends Transport {
private WebDavStore store; private WebDavStore store;
public WebDavTransport(Account account) throws MessagingException { public WebDavTransport(StoreConfig configInterface) throws MessagingException {
if (account.getRemoteStore() instanceof WebDavStore) { store = new WebDavStore(configInterface);
store = (WebDavStore) account.getRemoteStore();
} else {
store = new WebDavStore(account);
}
if (K9.DEBUG) if (K9.DEBUG)
Log.d(K9.LOG_TAG, ">>> New WebDavTransport creation complete"); Log.d(K9.LOG_TAG, ">>> New WebDavTransport creation complete");

View File

@ -25,9 +25,9 @@ import android.util.Xml;
import com.fsck.k9.Account; import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.Preferences; import com.fsck.k9.Preferences;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.store.RemoteStore;
import com.fsck.k9.preferences.Settings.InvalidSettingValueException; import com.fsck.k9.preferences.Settings.InvalidSettingValueException;
import com.fsck.k9.preferences.Settings.SettingsDescription; import com.fsck.k9.preferences.Settings.SettingsDescription;
@ -223,7 +223,7 @@ public class SettingsExporter {
// Write incoming server settings // Write incoming server settings
ServerSettings incoming = Store.decodeStoreUri(account.getStoreUri()); ServerSettings incoming = RemoteStore.decodeStoreUri(account.getStoreUri());
serializer.startTag(null, INCOMING_SERVER_ELEMENT); serializer.startTag(null, INCOMING_SERVER_ELEMENT);
serializer.attribute(null, TYPE_ATTRIBUTE, incoming.type); serializer.attribute(null, TYPE_ATTRIBUTE, incoming.type);

View File

@ -26,8 +26,8 @@ import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.AuthType; import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity; import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.store.RemoteStore;
import com.fsck.k9.mail.store.WebDavStore; import com.fsck.k9.mail.store.WebDavStore;
import com.fsck.k9.preferences.Settings.InvalidSettingValueException; import com.fsck.k9.preferences.Settings.InvalidSettingValueException;
@ -376,7 +376,7 @@ public class SettingsImporter {
// Write incoming server settings (storeUri) // Write incoming server settings (storeUri)
ServerSettings incoming = new ImportedServerSettings(account.incoming); ServerSettings incoming = new ImportedServerSettings(account.incoming);
String storeUri = Store.createStoreUri(incoming); String storeUri = RemoteStore.createStoreUri(incoming);
putString(editor, accountKeyPrefix + Account.STORE_URI_KEY, Utility.base64Encode(storeUri)); putString(editor, accountKeyPrefix + Account.STORE_URI_KEY, Utility.base64Encode(storeUri));
// Mark account as disabled if the AuthType isn't EXTERNAL and the // Mark account as disabled if the AuthType isn't EXTERNAL and the

View File

@ -215,7 +215,7 @@ public class AttachmentProvider extends ContentProvider {
final AttachmentInfo attachmentInfo; final AttachmentInfo attachmentInfo;
try { try {
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName); final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
attachmentInfo = LocalStore.getLocalInstance(account, K9.app).getAttachmentInfo(id); attachmentInfo = LocalStore.getInstance(account, K9.app).getAttachmentInfo(id);
} catch (MessagingException e) { } catch (MessagingException e) {
Log.e(K9.LOG_TAG, "Unable to retrieve attachment info from local store for ID: " + id, e); Log.e(K9.LOG_TAG, "Unable to retrieve attachment info from local store for ID: " + id, e);
return null; return null;
@ -269,7 +269,7 @@ public class AttachmentProvider extends ContentProvider {
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName); final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
try { try {
final LocalStore localStore = LocalStore.getLocalInstance(account, K9.app); final LocalStore localStore = LocalStore.getInstance(account, K9.app);
AttachmentInfo attachmentInfo = localStore.getAttachmentInfo(id); AttachmentInfo attachmentInfo = localStore.getAttachmentInfo(id);
if (FORMAT_VIEW.equals(format) && mimeType != null) { if (FORMAT_VIEW.equals(format) && mimeType != null) {

View File

@ -32,6 +32,7 @@ import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.store.local.LocalFolder;
import com.fsck.k9.mail.store.local.LocalMessage; import com.fsck.k9.mail.store.local.LocalMessage;
import com.fsck.k9.mail.store.local.LocalStore; import com.fsck.k9.mail.store.local.LocalStore;
import com.fsck.k9.search.SearchAccount; import com.fsck.k9.search.SearchAccount;
@ -200,9 +201,9 @@ public class MessageProvider extends ContentProvider {
public static class DeleteUriExtractor implements FieldExtractor<MessageInfoHolder, String> { public static class DeleteUriExtractor implements FieldExtractor<MessageInfoHolder, String> {
@Override @Override
public String getField(final MessageInfoHolder source) { public String getField(final MessageInfoHolder source) {
final Message message = source.message; final LocalMessage message = source.message;
return CONTENT_URI + "/delete_message/" return CONTENT_URI + "/delete_message/"
+ message.getFolder().getAccount().getAccountNumber() + "/" + message.getAccount().getAccountNumber() + "/"
+ message.getFolder().getName() + "/" + message.getUid(); + message.getFolder().getName() + "/" + message.getUid();
} }
} }
@ -221,21 +222,21 @@ public class MessageProvider extends ContentProvider {
public static class AccountExtractor implements FieldExtractor<MessageInfoHolder, String> { public static class AccountExtractor implements FieldExtractor<MessageInfoHolder, String> {
@Override @Override
public String getField(final MessageInfoHolder source) { public String getField(final MessageInfoHolder source) {
return source.message.getFolder().getAccount().getDescription(); return source.message.getAccount().getDescription();
} }
} }
public static class AccountColorExtractor implements FieldExtractor<MessageInfoHolder, Integer> { public static class AccountColorExtractor implements FieldExtractor<MessageInfoHolder, Integer> {
@Override @Override
public Integer getField(final MessageInfoHolder source) { public Integer getField(final MessageInfoHolder source) {
return source.message.getFolder().getAccount().getChipColor(); return source.message.getAccount().getChipColor();
} }
} }
public static class AccountNumberExtractor implements FieldExtractor<MessageInfoHolder, Integer> { public static class AccountNumberExtractor implements FieldExtractor<MessageInfoHolder, Integer> {
@Override @Override
public Integer getField(final MessageInfoHolder source) { public Integer getField(final MessageInfoHolder source) {
return source.message.getFolder().getAccount().getAccountNumber(); return source.message.getAccount().getAccountNumber();
} }
} }
@ -910,18 +911,18 @@ public class MessageProvider extends ContentProvider {
@Override @Override
public void listLocalMessagesAddMessages(final Account account, public void listLocalMessagesAddMessages(final Account account,
final String folderName, final List<Message> messages) { final String folderName, final List<LocalMessage> messages) {
// cache fields into local variables for faster access on JVM without JIT // cache fields into local variables for faster access on JVM without JIT
final MessageHelper helper = mMessageHelper; final MessageHelper helper = mMessageHelper;
final List<MessageInfoHolder> holders = mHolders; final List<MessageInfoHolder> holders = mHolders;
final Context context = getContext(); final Context context = getContext();
for (final Message message : messages) { for (final LocalMessage message : messages) {
final MessageInfoHolder messageInfoHolder = new MessageInfoHolder(); final MessageInfoHolder messageInfoHolder = new MessageInfoHolder();
final Folder messageFolder = message.getFolder(); final Folder messageFolder = message.getFolder();
final Account messageAccount = messageFolder.getAccount();
final Account messageAccount = messageInfoHolder.message.getAccount();
helper.populate(messageInfoHolder, message, new FolderInfoHolder(context, helper.populate(messageInfoHolder, message, new FolderInfoHolder(context,
messageFolder, messageAccount), messageAccount); messageFolder, messageAccount), messageAccount);
@ -1038,9 +1039,9 @@ public class MessageProvider extends ContentProvider {
} }
// get localstore parameter // get localstore parameter
Message msg = null; LocalMessage msg = null;
try { try {
Folder lf = LocalStore.getLocalInstance(myAccount, K9.app).getFolder(folderName); LocalFolder lf = LocalStore.getInstance(myAccount, K9.app).getFolder(folderName);
int msgCount = lf.getMessageCount(); int msgCount = lf.getMessageCount();
if (K9.DEBUG) { if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "folder msg count = " + msgCount); Log.d(K9.LOG_TAG, "folder msg count = " + msgCount);

View File

@ -10,9 +10,9 @@ import com.fsck.k9.Preferences;
import com.fsck.k9.activity.MessageCompose; import com.fsck.k9.activity.MessageCompose;
import com.fsck.k9.activity.MessageReference; import com.fsck.k9.activity.MessageReference;
import com.fsck.k9.controller.MessagingController; import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Message; import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.store.local.LocalMessage;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
@ -89,10 +89,10 @@ public class NotificationActionService extends CoreService {
List<MessageReference> refs = List<MessageReference> refs =
intent.getParcelableArrayListExtra(EXTRA_MESSAGE_LIST); intent.getParcelableArrayListExtra(EXTRA_MESSAGE_LIST);
List<Message> messages = new ArrayList<Message>(); List<LocalMessage> messages = new ArrayList<LocalMessage>();
for (MessageReference ref : refs) { for (MessageReference ref : refs) {
Message m = ref.restoreToLocalMessage(this); LocalMessage m = ref.restoreToLocalMessage(this);
if (m != null) { if (m != null) {
messages.add(m); messages.add(m);
} }
@ -106,7 +106,7 @@ public class NotificationActionService extends CoreService {
MessageReference ref = (MessageReference) intent.getParcelableExtra(EXTRA_MESSAGE); MessageReference ref = (MessageReference) intent.getParcelableExtra(EXTRA_MESSAGE);
Message message = ref.restoreToLocalMessage(this); Message message = ref.restoreToLocalMessage(this);
if (message != null) { if (message != null) {
Intent i = MessageCompose.getActionReplyIntent(this, account, message, false, null); Intent i = MessageCompose.getActionReplyIntent(this, message, false, null);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i); startActivity(i);
} else { } else {

View File

@ -6,14 +6,13 @@ import java.util.Map;
import com.fsck.k9.mail.AuthType; import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity; import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.store.ImapStore;
import junit.framework.TestCase; import junit.framework.TestCase;
public class ImapStoreUriTest extends TestCase { public class ImapStoreUriTest extends TestCase {
public void testDecodeStoreUriImapAllExtras() { public void testDecodeStoreUriImapAllExtras() {
String uri = "imap://PLAIN:user:pass@server:143/0%7CcustomPathPrefix"; String uri = "imap://PLAIN:user:pass@server:143/0%7CcustomPathPrefix";
ServerSettings settings = Store.decodeStoreUri(uri); ServerSettings settings = RemoteStore.decodeStoreUri(uri);
assertEquals(AuthType.PLAIN, settings.authenticationType); assertEquals(AuthType.PLAIN, settings.authenticationType);
assertEquals("user", settings.username); assertEquals("user", settings.username);
@ -26,7 +25,7 @@ public class ImapStoreUriTest extends TestCase {
public void testDecodeStoreUriImapNoExtras() { public void testDecodeStoreUriImapNoExtras() {
String uri = "imap://PLAIN:user:pass@server:143/"; String uri = "imap://PLAIN:user:pass@server:143/";
ServerSettings settings = Store.decodeStoreUri(uri); ServerSettings settings = RemoteStore.decodeStoreUri(uri);
assertEquals(AuthType.PLAIN, settings.authenticationType); assertEquals(AuthType.PLAIN, settings.authenticationType);
assertEquals("user", settings.username); assertEquals("user", settings.username);
@ -38,7 +37,7 @@ public class ImapStoreUriTest extends TestCase {
public void testDecodeStoreUriImapPrefixOnly() { public void testDecodeStoreUriImapPrefixOnly() {
String uri = "imap://PLAIN:user:pass@server:143/customPathPrefix"; String uri = "imap://PLAIN:user:pass@server:143/customPathPrefix";
ServerSettings settings = Store.decodeStoreUri(uri); ServerSettings settings = RemoteStore.decodeStoreUri(uri);
assertEquals(AuthType.PLAIN, settings.authenticationType); assertEquals(AuthType.PLAIN, settings.authenticationType);
assertEquals("user", settings.username); assertEquals("user", settings.username);
@ -51,7 +50,7 @@ public class ImapStoreUriTest extends TestCase {
public void testDecodeStoreUriImapEmptyPrefix() { public void testDecodeStoreUriImapEmptyPrefix() {
String uri = "imap://PLAIN:user:pass@server:143/0%7C"; String uri = "imap://PLAIN:user:pass@server:143/0%7C";
ServerSettings settings = Store.decodeStoreUri(uri); ServerSettings settings = RemoteStore.decodeStoreUri(uri);
assertEquals(AuthType.PLAIN, settings.authenticationType); assertEquals(AuthType.PLAIN, settings.authenticationType);
assertEquals("user", settings.username); assertEquals("user", settings.username);
@ -64,7 +63,7 @@ public class ImapStoreUriTest extends TestCase {
public void testDecodeStoreUriImapAutodetectAndPrefix() { public void testDecodeStoreUriImapAutodetectAndPrefix() {
String uri = "imap://PLAIN:user:pass@server:143/1%7CcustomPathPrefix"; String uri = "imap://PLAIN:user:pass@server:143/1%7CcustomPathPrefix";
ServerSettings settings = Store.decodeStoreUri(uri); ServerSettings settings = RemoteStore.decodeStoreUri(uri);
assertEquals(AuthType.PLAIN, settings.authenticationType); assertEquals(AuthType.PLAIN, settings.authenticationType);
assertEquals("user", settings.username); assertEquals("user", settings.username);
@ -84,7 +83,7 @@ public class ImapStoreUriTest extends TestCase {
ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143, ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143,
ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null, extra); ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null, extra);
String uri = Store.createStoreUri(settings); String uri = RemoteStore.createStoreUri(settings);
assertEquals("imap://PLAIN:user:pass@server:143/0%7CcustomPathPrefix", uri); assertEquals("imap://PLAIN:user:pass@server:143/0%7CcustomPathPrefix", uri);
} }
@ -97,7 +96,7 @@ public class ImapStoreUriTest extends TestCase {
ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143, ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143,
ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null, extra); ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null, extra);
String uri = Store.createStoreUri(settings); String uri = RemoteStore.createStoreUri(settings);
assertEquals("imap://PLAIN:user:pass@server:143/0%7C", uri); assertEquals("imap://PLAIN:user:pass@server:143/0%7C", uri);
} }
@ -106,7 +105,7 @@ public class ImapStoreUriTest extends TestCase {
ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143, ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143,
ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null); ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null);
String uri = Store.createStoreUri(settings); String uri = RemoteStore.createStoreUri(settings);
assertEquals("imap://PLAIN:user:pass@server:143/1%7C", uri); assertEquals("imap://PLAIN:user:pass@server:143/1%7C", uri);
} }
@ -118,7 +117,7 @@ public class ImapStoreUriTest extends TestCase {
ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143, ServerSettings settings = new ServerSettings(ImapStore.STORE_TYPE, "server", 143,
ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null, extra); ConnectionSecurity.NONE, AuthType.PLAIN, "user", "pass", null, extra);
String uri = Store.createStoreUri(settings); String uri = RemoteStore.createStoreUri(settings);
assertEquals("imap://PLAIN:user:pass@server:143/1%7C", uri); assertEquals("imap://PLAIN:user:pass@server:143/1%7C", uri);
} }