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.Store;
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.StorageProvider;
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
* 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)
*/
@ -1289,11 +1291,11 @@ public class Account implements BaseAccount {
}
public LocalStore getLocalStore() throws MessagingException {
return Store.getLocalInstance(this, K9.app);
return LocalStore.getInstance(this, K9.app);
}
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

View File

@ -14,6 +14,7 @@ import android.content.SharedPreferences;
import android.util.Log;
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.Storage;
@ -121,7 +122,7 @@ public class Preferences {
accountsInOrder.remove(account);
}
Store.removeAccount(account);
LocalStore.removeAccount(account);
account.deleteCertificates();
account.delete(this);

View File

@ -5,7 +5,6 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.EnumSet;
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.controller.MessagingController;
import com.fsck.k9.helper.SizeFormatter;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.internet.MimeUtility;
import com.fsck.k9.mail.store.RemoteStore;
import com.fsck.k9.mail.store.StorageManager;
import com.fsck.k9.mail.store.WebDavStore;
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) {
ServerSettings incoming = Store.decodeStoreUri(mAccount.getStoreUri());
ServerSettings incoming = RemoteStore.decodeStoreUri(mAccount.getStoreUri());
ServerSettings outgoing = Transport.decodeTransportUri(mAccount.getTransportUri());
/*
@ -992,9 +990,9 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
if (mIncomingPassword != null) {
// Set incoming server password
String storeUri = mAccount.getStoreUri();
ServerSettings incoming = Store.decodeStoreUri(storeUri);
ServerSettings incoming = RemoteStore.decodeStoreUri(storeUri);
ServerSettings newIncoming = incoming.newPassword(mIncomingPassword);
String newStoreUri = Store.createStoreUri(newIncoming);
String newStoreUri = RemoteStore.createStoreUri(newIncoming);
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.TextBodyBuilder;
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.TempFileMessageBody;
import com.fsck.k9.view.MessageWebView;
@ -435,17 +436,15 @@ 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
* the function is reply all instead of simply reply.
* @param context
* @param account
* @param message
* @param replyAll
* @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message
*/
public static Intent getActionReplyIntent(
Context context,
Account account,
Message message,
boolean replyAll,
String messageBody) {
Context context,
Message message,
boolean replyAll,
String messageBody) {
Intent i = new Intent(context, MessageCompose.class);
i.putExtra(EXTRA_MESSAGE_BODY, messageBody);
i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference());
@ -468,25 +467,22 @@ public class MessageCompose extends K9Activity implements OnClickListener,
*/
public static void actionReply(
Context context,
Account account,
Message message,
boolean replyAll,
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.
* @param context
* @param account
* @param message
* @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message
*/
public static void actionForward(
Context context,
Account account,
Message message,
String messageBody) {
Context context,
Message message,
String messageBody) {
Intent i = new Intent(context, MessageCompose.class);
i.putExtra(EXTRA_MESSAGE_BODY, messageBody);
i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference());
@ -2646,7 +2642,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
* @param message
* The source message used to populate the various text fields.
*/
private void processSourceMessage(Message message) {
private void processSourceMessage(LocalMessage message) {
try {
switch (mAction) {
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";
mDraftId = MessagingController.getInstance(getApplication()).getId(message);
@ -2852,7 +2848,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
newIdentity.setSignature(k9identity.get(IdentityField.SIGNATURE));
mSignatureChanged = true;
} else {
newIdentity.setSignatureUse(message.getFolder().getAccount().getSignatureUse());
newIdentity.setSignatureUse(message.getFolder().getSignatureUse());
newIdentity.setSignature(mIdentity.getSignature());
}
@ -3437,7 +3433,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
}
updateMessageFormat();
} else {
processSourceMessage(message);
processSourceMessage((LocalMessage) message);
mSourceProcessed = true;
}
}

View File

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

View File

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

View File

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

View File

@ -38,9 +38,9 @@ import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.AuthType;
import com.fsck.k9.mail.ConnectionSecurity;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Transport;
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.view.ClientCertificateSpinner;
import com.fsck.k9.view.ClientCertificateSpinner.OnClientCertificateChangedListener;
@ -421,7 +421,7 @@ public class AccountSetupBasics extends K9Activity
ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias);
ServerSettings transportServer = new ServerSettings(SmtpTransport.TRANSPORT_TYPE, "mail." + domain, -1,
ConnectionSecurity.SSL_TLS_REQUIRED, authenticationType, user, password, clientCertificateAlias);
String storeUri = Store.createStoreUri(storeServer);
String storeUri = RemoteStore.createStoreUri(storeServer);
String transportUri = Transport.createTransportUri(transportServer);
mAccount.setStoreUri(storeUri);
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.store.ImapStore;
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.ImapStore.ImapStoreSettings;
import com.fsck.k9.mail.store.WebDavStore.WebDavStoreSettings;
@ -163,7 +164,7 @@ public class AccountSetupIncoming extends K9Activity implements OnClickListener
}
try {
ServerSettings settings = Store.decodeStoreUri(mAccount.getStoreUri());
ServerSettings settings = RemoteStore.decodeStoreUri(mAccount.getStoreUri());
if (savedInstanceState == null) {
// 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,
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_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) {
for (Message message : messages) {
LocalMessage localMessage = (LocalMessage) message;
long messageId = localMessage.getId();
long folderId = ((LocalFolder) localMessage.getFolder()).getId();
mHiddenMessageCache.put(messageId, folderId);
for (LocalMessage message : messages) {
long messageId = message.getId();
mHiddenMessageCache.put(messageId, message.getFolder().getId());
}
}

View File

@ -3,10 +3,10 @@ package com.fsck.k9.controller;
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 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>

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 void suppressMessages(Account account, List<Message> messages) {
private void suppressMessages(Account account, List<LocalMessage> messages) {
EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(),
mApplication.getApplicationContext());
cache.hideMessages(messages);
@ -324,13 +324,11 @@ public class MessagingController implements Runnable {
cache.unhideMessages(messages);
}
private boolean isMessageSuppressed(Account account, Message message) {
LocalMessage localMessage = (LocalMessage) message;
String accountUuid = account.getUuid();
long messageId = localMessage.getId();
long folderId = ((LocalFolder) localMessage.getFolder()).getId();
private boolean isMessageSuppressed(LocalMessage message) {
long messageId = message.getId();
long folderId = message.getFolder().getId();
EmailProviderCache cache = EmailProviderCache.getCache(accountUuid,
EmailProviderCache cache = EmailProviderCache.getCache(message.getFolder().getUuid(),
mApplication.getApplicationContext());
return cache.isMessageHidden(messageId, folderId);
}
@ -690,15 +688,15 @@ public class MessagingController implements Runnable {
}
// Collecting statistics of the search result
MessageRetrievalListener retrievalListener = new MessageRetrievalListener() {
MessageRetrievalListener retrievalListener = new MessageRetrievalListener<LocalMessage>() {
@Override
public void messageStarted(String message, int number, int ofTotal) {}
@Override
public void messagesFinished(int number) {}
@Override
public void messageFinished(Message message, int number, int ofTotal) {
if (!isMessageSuppressed(message.getFolder().getAccount(), message)) {
List<Message> messages = new ArrayList<Message>();
public void messageFinished(LocalMessage message, int number, int ofTotal) {
if (!isMessageSuppressed(message)) {
List<LocalMessage> messages = new ArrayList<LocalMessage>();
messages.add(message);
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);
if (listener != null) {
listener.remoteSearchStarted(acct, folderName);
listener.remoteSearchStarted(folderName);
}
List<Message> extraResults = new ArrayList<Message>();
@ -791,7 +789,7 @@ public class MessagingController implements Runnable {
messages.clear();
if (listener != null) {
listener.remoteSearchServerQueryComplete(acct, folderName, remoteMessages.size());
listener.remoteSearchServerQueryComplete(folderName, remoteMessages.size(), acct.getRemoteSearchNumResults());
}
Collections.sort(remoteMessages, new UidReverseComparator());
@ -811,13 +809,13 @@ public class MessagingController implements Runnable {
} else {
Log.e(K9.LOG_TAG, "Could not complete remote search", e);
if (listener != null) {
listener.remoteSearchFailed(acct, null, e.getMessage());
listener.remoteSearchFailed(null, e.getMessage());
}
addErrorMessage(acct, null, e);
}
} finally {
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) {
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,
List<Message> unsyncedMessages,
final List<Message> smallMessages,
@ -1473,9 +1471,9 @@ public class MessagingController implements Runnable {
final List<Message> chunk = new ArrayList<Message>(UNSYNC_CHUNK_SIZE);
remoteFolder.fetch(unsyncedMessages, fp,
new MessageRetrievalListener() {
new MessageRetrievalListener<T>() {
@Override
public void messageFinished(Message message, int number, int ofTotal) {
public void messageFinished(T message, int number, int ofTotal) {
try {
String newPushState = remoteFolder.getNewPushState(localFolder.getPushState(), message);
if (newPushState != null) {
@ -1564,7 +1562,7 @@ public class MessagingController implements Runnable {
localFolder.appendMessages(messages);
for (final Message message : messages) {
final Message localMessage = localFolder.getMessage(message.getUid());
final LocalMessage localMessage = localFolder.getMessage(message.getUid());
syncFlags(localMessage, message);
if (K9.DEBUG)
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;
}
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,
List<Message> smallMessages,
List<T> smallMessages,
final AtomicInteger progress,
final int unreadBeforeStart,
final AtomicInteger newMessages,
@ -1608,9 +1606,9 @@ public class MessagingController implements Runnable {
Log.d(K9.LOG_TAG, "SYNC: Fetching small messages for folder " + folder);
remoteFolder.fetch(smallMessages,
fp, new MessageRetrievalListener() {
fp, new MessageRetrievalListener<T>() {
@Override
public void messageFinished(final Message message, int number, int ofTotal) {
public void messageFinished(final T message, int number, int ofTotal) {
try {
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,
List<Message> largeMessages,
List<T> largeMessages,
final AtomicInteger progress,
final int unreadBeforeStart,
final AtomicInteger newMessages,
@ -1821,11 +1819,11 @@ public class MessagingController implements Runnable {
remoteFolder.fetch(undeletedMessages, fp, null);
for (Message remoteMessage : syncFlagMessages) {
Message localMessage = localFolder.getMessage(remoteMessage.getUid());
LocalMessage localMessage = localFolder.getMessage(remoteMessage.getUid());
boolean messageChanged = syncFlags(localMessage, remoteMessage);
if (messageChanged) {
boolean shouldBeNotifiedOf = false;
if (localMessage.isSet(Flag.DELETED) || isMessageSuppressed(account, localMessage)) {
if (localMessage.isSet(Flag.DELETED) || isMessageSuppressed(localMessage)) {
for (MessagingListener l : getListeners()) {
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;
if (localMessage == null || localMessage.isSet(Flag.DELETED)) {
return false;
}
if (remoteMessage.isSet(Flag.DELETED)) {
if (localMessage.getFolder().getAccount().syncRemoteDeletions()) {
if (localMessage.getFolder().syncRemoteDeletions()) {
localMessage.setFlag(Flag.DELETED, true);
messageChanged = true;
}
@ -2857,7 +2855,7 @@ public class MessagingController implements Runnable {
* @param newState
* {@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) {
// TODO: Put this into the background, but right now some callers depend on the message
// 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,
final List<Message> messages, final String destFolder,
final List<LocalMessage> messages, final String destFolder,
final MessagingListener listener) {
suppressMessages(account, messages);
@ -3790,7 +3788,7 @@ public class MessagingController implements Runnable {
}
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);
@ -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) {
moveMessages(account, srcFolder, Collections.singletonList(message), destFolder, listener);
}
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) {
putBackground("copyMessages", null, new Runnable() {
@ -3828,7 +3826,7 @@ public class MessagingController implements Runnable {
}
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() {
@Override
@ -3851,7 +3849,7 @@ public class MessagingController implements Runnable {
}
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) {
try {
@ -3962,7 +3960,7 @@ public class MessagingController implements Runnable {
localFolder.open(Folder.OPEN_MODE_RW);
String uid = localFolder.getMessageUidById(id);
if (uid != null) {
Message message = localFolder.getMessage(uid);
LocalMessage message = localFolder.getMessage(uid);
if (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() {
@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 {
LocalStore localStore = account.getLocalStore();
@ -4025,7 +4023,7 @@ public class MessagingController implements Runnable {
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() {
@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>>>();
for (Message message : messages) {
for (LocalMessage message : messages) {
if ( message == null) {
continue;
}
Folder folder = message.getFolder();
Account account = folder.getAccount();
Account account = message.getAccount();
Map<Folder, List<Message>> folderMap = accountMap.get(account);
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.Message;
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.
@ -42,10 +43,8 @@ public class MessagingListener {
public void listLocalMessagesStarted(Account account, String folder) {}
public void listLocalMessages(Account account, String folder, Message[] messages) {}
public void listLocalMessagesAddMessages(Account account, String folder,
List<Message> messages) {}
List<LocalMessage> messages) {}
public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {}
@ -156,10 +155,9 @@ public class MessagingListener {
/**
* Called when a remote search is started
*
* @param acct
* @param folder
*/
public void remoteSearchStarted(Account acct, String folder) {}
public void remoteSearchStarted(String folder) {}
/**
@ -167,35 +165,30 @@ public class MessagingListener {
*
* @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
* Can assume headers have been downloaded, but potentially not body.
* @param account
* @param folder
* @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
*
* @param acct
* @param folder
* @param folder
* @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.
*
* @param acct
* @param folder
* @param folder
* @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

View File

@ -89,6 +89,7 @@ import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Message;
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.LocalStore;
import com.fsck.k9.provider.EmailProvider;
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
* 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 */
MessageHelper mMessageHelper;
@ -1035,7 +1036,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
return null;
}
private Folder getFolderById(Account account, long folderId) {
private LocalFolder getFolderById(Account account, long folderId) {
try {
LocalStore localStore = account.getLocalStore();
LocalFolder localFolder = localStore.getFolderById(folderId);
@ -1309,11 +1310,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
changeSort(sorts[curIndex]);
}
private void onDelete(Message message) {
private void onDelete(LocalMessage message) {
onDelete(Collections.singletonList(message));
}
private void onDelete(List<Message> messages) {
private void onDelete(List<LocalMessage> messages) {
if (K9.confirmDelete()) {
// remember the message selection for #onCreateDialog(int)
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) {
mController.deleteThreads(messages);
} else {
@ -1345,15 +1346,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
}
final String destFolderName = data.getStringExtra(ChooseFolder.EXTRA_NEW_FOLDER);
final List<Message> messages = mActiveMessages;
final List<LocalMessage> messages = mActiveMessages;
if (destFolderName != null) {
mActiveMessages = null; // don't need it any more
if (messages.size() > 0) {
Account account = messages.get(0).getFolder().getAccount();
account.setLastSelectedFolderName(destFolderName);
messages.get(0).getFolder().setLastSelectedFolderName(destFolderName);
}
switch (requestCode) {
@ -1539,7 +1539,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
break;
}
case R.id.delete: {
Message message = getMessageAtPosition(adapterPosition);
LocalMessage message = getMessageAtPosition(adapterPosition);
onDelete(message);
break;
}
@ -1562,23 +1562,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
// only if the account supports this
case R.id.archive: {
Message message = getMessageAtPosition(adapterPosition);
onArchive(message);
onArchive(getMessageAtPosition(adapterPosition));
break;
}
case R.id.spam: {
Message message = getMessageAtPosition(adapterPosition);
onSpam(message);
onSpam(getMessageAtPosition(adapterPosition));
break;
}
case R.id.move: {
Message message = getMessageAtPosition(adapterPosition);
onMove(message);
onMove(getMessageAtPosition(adapterPosition));
break;
}
case R.id.copy: {
Message message = getMessageAtPosition(adapterPosition);
onCopy(message);
onCopy(getMessageAtPosition(adapterPosition));
break;
}
}
@ -1711,7 +1707,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
class MessageListActivityListener extends ActivityListener {
@Override
public void remoteSearchFailed(Account acct, String folder, final String err) {
public void remoteSearchFailed(String folder, final String err) {
mHandler.post(new Runnable() {
@Override
public void run() {
@ -1725,7 +1721,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
}
@Override
public void remoteSearchStarted(Account acct, String folder) {
public void remoteSearchStarted(String folder) {
mHandler.progress(true);
mHandler.updateFooter(mContext.getString(R.string.remote_search_sending_query));
}
@ -1736,12 +1732,12 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
}
@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.remoteSearchFinished();
mExtraSearchResults = extraResults;
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 {
mHandler.updateFooter("");
}
@ -1750,11 +1746,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
}
@Override
public void remoteSearchServerQueryComplete(Account account, String folderName, int numResults) {
public void remoteSearchServerQueryComplete(String folderName, int numResults, int maxResults) {
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,
account.getRemoteSearchNumResults(), numResults));
maxResults, numResults));
} else {
mHandler.updateFooter(mContext.getString(R.string.remote_search_downloading, numResults));
}
@ -2416,7 +2412,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
computeBatchDirection();
}
private void onMove(Message message) {
private void onMove(LocalMessage message) {
onMove(Collections.singletonList(message));
}
@ -2426,7 +2422,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param messages
* Never {@code null}.
*/
private void onMove(List<Message> messages) {
private void onMove(List<LocalMessage> messages) {
if (!checkCopyOrMovePossible(messages, FolderOperation.MOVE)) {
return;
}
@ -2440,12 +2436,13 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
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));
}
@ -2455,7 +2452,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param messages
* Never {@code null}.
*/
private void onCopy(List<Message> messages) {
private void onCopy(List<LocalMessage> messages) {
if (!checkCopyOrMovePossible(messages, FolderOperation.COPY)) {
return;
}
@ -2469,7 +2466,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
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)
*/
private void displayFolderChoice(int requestCode, Account account, Folder folder,
List<Message> messages) {
private void displayFolderChoice(int requestCode, Folder folder,
String accountId, String lastSelectedFolderName,
List<LocalMessage> messages) {
Intent intent = new Intent(getActivity(), ChooseFolder.class);
intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, account.getUuid());
intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, account.getLastSelectedFolderName());
intent.putExtra(ChooseFolder.EXTRA_ACCOUNT, accountId);
intent.putExtra(ChooseFolder.EXTRA_SEL_FOLDER, lastSelectedFolderName);
if (folder == null) {
intent.putExtra(ChooseFolder.EXTRA_SHOW_CURRENT, "yes");
@ -2504,14 +2505,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
startActivityForResult(intent, requestCode);
}
private void onArchive(final Message message) {
private void onArchive(final LocalMessage message) {
onArchive(Collections.singletonList(message));
}
private void onArchive(final List<Message> messages) {
Map<Account, List<Message>> messagesByAccount = groupMessagesByAccount(messages);
private void onArchive(final List<LocalMessage> 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();
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) {
Map<Account, List<Message>> messagesByAccount = new HashMap<Account, List<Message>>();
for (Message message : messages) {
Account account = message.getFolder().getAccount();
private Map<Account, List<LocalMessage>> groupMessagesByAccount(final List<LocalMessage> messages) {
Map<Account, List<LocalMessage>> messagesByAccount = new HashMap<Account, List<LocalMessage>>();
for (LocalMessage message : messages) {
Account account = message.getAccount();
List<Message> msgList = messagesByAccount.get(account);
List<LocalMessage> msgList = messagesByAccount.get(account);
if (msgList == null) {
msgList = new ArrayList<Message>();
msgList = new ArrayList<LocalMessage>();
messagesByAccount.put(account, msgList);
}
@ -2537,7 +2538,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
return messagesByAccount;
}
private void onSpam(Message message) {
private void onSpam(LocalMessage message) {
onSpam(Collections.singletonList(message));
}
@ -2547,7 +2548,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param messages
* 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()) {
// remember the message selection for #onCreateDialog(int)
mActiveMessages = messages;
@ -2557,10 +2558,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
}
}
private void onSpamConfirmed(List<Message> messages) {
Map<Account, List<Message>> messagesByAccount = groupMessagesByAccount(messages);
private void onSpamConfirmed(List<LocalMessage> 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();
String spamFolder = account.getSpamFolderName();
@ -2584,7 +2585,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
*
* @return {@code true}, if operation is possible.
*/
private boolean checkCopyOrMovePossible(final List<Message> messages,
private boolean checkCopyOrMovePossible(final List<LocalMessage> messages,
final FolderOperation operation) {
if (messages.isEmpty()) {
@ -2592,11 +2593,11 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
}
boolean first = true;
for (final Message message : messages) {
for (final LocalMessage message : messages) {
if (first) {
first = false;
// account check
final Account account = message.getFolder().getAccount();
final Account account = message.getAccount();
if ((operation == FolderOperation.MOVE && !mController.isMoveCapable(account)) ||
(operation == FolderOperation.COPY && !mController.isCopyCapable(account))) {
return false;
@ -2622,7 +2623,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param destination
* 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);
}
@ -2634,7 +2635,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param destination
* 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);
}
@ -2650,12 +2651,12 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
* @param operation
* 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) {
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)) ||
(operation == FolderOperation.COPY && !mController.isCopyCapable(message))) {
@ -2674,19 +2675,19 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
continue;
}
List<Message> outMessages = folderMap.get(folderName);
List<LocalMessage> outMessages = folderMap.get(folderName);
if (outMessages == null) {
outMessages = new ArrayList<Message>();
outMessages = new ArrayList<LocalMessage>();
folderMap.put(folderName, outMessages);
}
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();
List<Message> outMessages = entry.getValue();
Account account = outMessages.get(0).getFolder().getAccount();
List<LocalMessage> outMessages = entry.getValue();
Account account = outMessages.get(0).getAccount();
if (operation == FolderOperation.MOVE) {
if (mThreadedList) {
@ -2859,7 +2860,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
*/
switch (item.getItemId()) {
case R.id.delete: {
List<Message> messages = getCheckedMessages();
List<LocalMessage> messages = getCheckedMessages();
onDelete(messages);
mSelectedCount = 0;
break;
@ -2887,26 +2888,22 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
// only if the account supports this
case R.id.archive: {
List<Message> messages = getCheckedMessages();
onArchive(messages);
onArchive(getCheckedMessages());
mSelectedCount = 0;
break;
}
case R.id.spam: {
List<Message> messages = getCheckedMessages();
onSpam(messages);
onSpam(getCheckedMessages());
mSelectedCount = 0;
break;
}
case R.id.move: {
List<Message> messages = getCheckedMessages();
onMove(messages);
onMove(getCheckedMessages());
mSelectedCount = 0;
break;
}
case R.id.copy: {
List<Message> messages = getCheckedMessages();
onCopy(messages);
onCopy(getCheckedMessages());
mSelectedCount = 0;
break;
}
@ -2987,7 +2984,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
final Folder remoteFolder = mCurrentFolder.folder;
remoteFolder.close();
// 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) {
// 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);
@ -3135,7 +3132,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
changeSort(mSortType);
}
private Message getSelectedMessage() {
private LocalMessage getSelectedMessage() {
int listViewPosition = mListView.getSelectedItemPosition();
int adapterPosition = listViewToAdapterPosition(listViewPosition);
@ -3158,7 +3155,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
return AdapterView.INVALID_POSITION;
}
private Message getMessageAtPosition(int adapterPosition) {
private LocalMessage getMessageAtPosition(int adapterPosition) {
if (adapterPosition == AdapterView.INVALID_POSITION) {
return null;
}
@ -3168,7 +3165,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
Account account = getAccountFromCursor(cursor);
long folderId = cursor.getLong(FOLDER_ID_COLUMN);
Folder folder = getFolderById(account, folderId);
LocalFolder folder = getFolderById(account, folderId);
try {
return folder.getMessage(uid);
@ -3179,14 +3176,14 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
return null;
}
private List<Message> getCheckedMessages() {
List<Message> messages = new ArrayList<Message>(mSelected.size());
private List<LocalMessage> getCheckedMessages() {
List<LocalMessage> messages = new ArrayList<LocalMessage>(mSelected.size());
for (int position = 0, end = mAdapter.getCount(); position < end; position++) {
Cursor cursor = (Cursor) mAdapter.getItem(position);
long uniqueId = cursor.getLong(mUniqueIdColumn);
if (mSelected.contains(uniqueId)) {
Message message = getMessageAtPosition(position);
LocalMessage message = getMessageAtPosition(position);
if (message != null) {
messages.add(message);
}
@ -3197,7 +3194,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
}
public void onDelete() {
Message message = getSelectedMessage();
LocalMessage message = getSelectedMessage();
if (message != null) {
onDelete(Collections.singletonList(message));
}
@ -3227,21 +3224,21 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
}
public void onMove() {
Message message = getSelectedMessage();
LocalMessage message = getSelectedMessage();
if (message != null) {
onMove(message);
}
}
public void onArchive() {
Message message = getSelectedMessage();
LocalMessage message = getSelectedMessage();
if (message != null) {
onArchive(message);
}
}
public void onCopy() {
Message message = getSelectedMessage();
LocalMessage message = getSelectedMessage();
if (message != null) {
onCopy(message);
}

View File

@ -75,7 +75,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
private PgpData mPgpData;
private Account mAccount;
private MessageReference mMessageReference;
private Message mMessage;
private LocalMessage mMessage;
private MessagingController mController;
private Listener mListener = new Listener();
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
// accidental clicks)
mFragmentListener.disableDeleteAction();
Message messageToDelete = mMessage;
LocalMessage messageToDelete = mMessage;
mFragmentListener.showNextMessageOrReturn();
mController.deleteMessages(Collections.singletonList(messageToDelete), null);
}
@ -341,7 +341,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
private void refileMessage(String dstFolder) {
String srcFolder = mMessageReference.folderName;
Message messageToMove = mMessage;
LocalMessage messageToMove = mMessage;
mFragmentListener.showNextMessageOrReturn();
mController.moveMessage(mAccount, srcFolder, messageToMove, dstFolder, null);
}
@ -576,7 +576,8 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
@Override
public void loadMessageForViewBodyAvailable(final Account account, String folder,
String uid, final Message message) {
if (!mMessageReference.uid.equals(uid) ||
if (!(message instanceof LocalMessage) ||
!mMessageReference.uid.equals(uid) ||
!mMessageReference.folderName.equals(folder) ||
!mMessageReference.accountUuid.equals(account.getUuid())) {
return;
@ -586,7 +587,7 @@ public class MessageViewFragment extends Fragment implements OnClickListener,
@Override
public void run() {
try {
mMessage = message;
mMessage = (LocalMessage) message;
mMessageView.setMessage(account, (LocalMessage) message, mPgpData,
mController, mListener);
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.MessagingException;
import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mail.store.local.LocalMessage;
public class MessageHelper {
@ -32,8 +33,10 @@ public class MessageHelper {
mContext = context;
}
public void populate(final MessageInfoHolder target, final Message message,
final FolderInfoHolder folder, final Account account) {
public void populate(final MessageInfoHolder target,
final LocalMessage message,
final FolderInfoHolder folder,
Account account) {
final Contacts contactHelper = K9.showContactName() ? Contacts.getInstance(mContext) : null;
try {
target.message = message;
@ -68,14 +71,9 @@ public class MessageHelper {
target.senderAddress = target.compareCounterparty;
}
target.uid = message.getUid();
target.account = account.getUuid();
target.uri = "email://messages/" + account.getAccountNumber() + "/" + message.getFolder().getName() + "/" + message.getUid();
target.account = message.getFolder().getUuid();
target.uri = message.getUri();
} catch (MessagingException me) {
Log.w(K9.LOG_TAG, "Unable to load message info", me);
}

View File

@ -1,21 +1,17 @@
package com.fsck.k9.mail;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.controller.MessageRetrievalListener;
public abstract class Folder {
protected final Account mAccount;
public abstract class Folder<T extends Message> {
private String status = null;
private long lastChecked = 0;
private long lastPush = 0;
@ -32,10 +28,6 @@ public abstract class Folder {
HOLDS_FOLDERS, HOLDS_MESSAGES,
}
protected Folder(Account account) {
mAccount = account;
}
/**
* Forces an open of the MailProvider. If the provider is already open this
* function returns without doing anything.
@ -83,7 +75,7 @@ public abstract class Folder {
public abstract int getUnreadMessageCount() 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.
@ -94,7 +86,7 @@ public abstract class Folder {
* @return List of messages
* @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
@ -104,13 +96,13 @@ public abstract class Folder {
* @param listener Listener to notify as we download 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);
}
public abstract List<? extends Message> getMessages(String[] uids, MessageRetrievalListener listener)
public abstract List<T> getMessages(String[] uids, MessageRetrievalListener<T> listener)
throws MessagingException;
public abstract Map<String, String> appendMessages(List<? extends Message> messages) throws MessagingException;
@ -149,10 +141,10 @@ public abstract class Folder {
* @throws MessagingException
*/
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,
MessageRetrievalListener listener) throws MessagingException {
MessageRetrievalListener<T> listener) throws MessagingException {
// This is causing trouble. Disabled for now. See issue 1733
//throw new RuntimeException("fetchPart() not implemented.");
@ -241,10 +233,6 @@ public abstract class Folder {
this.status = status;
}
public Account getAccount() {
return mAccount;
}
public List<Message> search(String queryString, final Set<Flag> requiredFlags, final Set<Flag> forbiddenFlags)
throws MessagingException {
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 {
protected MessageReference mReference;
private MessageReference mReference = null;
public enum RecipientType {
TO, CC, BCC,
@ -55,8 +55,7 @@ public abstract class Message implements Part, CompositeBody {
}
Message other = (Message)o;
return (mUid.equals(other.getUid())
&& mFolder.getName().equals(other.getFolder().getName())
&& mFolder.getAccount().getUuid().equals(other.getFolder().getAccount().getUuid()));
&& mFolder.getName().equals(other.getFolder().getName()));
}
@Override
@ -65,7 +64,6 @@ public abstract class Message implements Part, CompositeBody {
int result = 1;
result = MULTIPLIER * result + mFolder.getName().hashCode();
result = MULTIPLIER * result + mFolder.getAccount().getUuid().hashCode();
result = MULTIPLIER * result + mUid.hashCode();
return result;
}
@ -75,7 +73,7 @@ public abstract class Message implements Part, CompositeBody {
}
public void setUid(String uid) {
mReference = null;
this.mReference = null;
this.mUid = uid;
}
@ -249,15 +247,14 @@ public abstract class Message implements Part, CompositeBody {
public void destroy() throws MessagingException {}
@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 MessageReference makeMessageReference() {
if (mReference == null) {
mReference = new MessageReference();
mReference.accountUuid = getFolder().getAccount().getUuid();
mReference.folderName = getFolder().getName();
mReference.folderName = getFolder().getName();
mReference.uid = mUid;
}
return mReference;

View File

@ -1,24 +1,7 @@
package com.fsck.k9.mail;
import java.util.HashMap;
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
@ -29,191 +12,6 @@ import com.fsck.k9.mail.store.WebDavStore;
* making as few network connections as possible.
*/
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 List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException;
@ -244,14 +42,9 @@ public abstract class Store {
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) {
return null;
}
public Account getAccount() {
return mAccount;
}
}

View File

@ -11,7 +11,7 @@ import java.util.ArrayList;
import java.util.Date;
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 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);

View File

@ -55,7 +55,6 @@ import android.os.PowerManager;
import android.text.TextUtils;
import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.R;
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.Pusher;
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.EOLConvertingOutputStream;
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
* </pre>
*/
public class ImapStore extends Store {
public class ImapStore extends RemoteStore {
public static final String STORE_TYPE = "IMAP";
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 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);
@ -387,7 +385,7 @@ public class ImapStore extends Store {
@Override
public boolean useCompression(final int type) {
return mAccount.useCompression(type);
return mStoreConfig.useCompression(type);
}
@Override
@ -439,12 +437,12 @@ public class ImapStore extends Store {
*/
private final Map<String, ImapFolder> mFolderCache = new HashMap<String, ImapFolder>();
public ImapStore(Account account) throws MessagingException {
super(account);
public ImapStore(StoreConfig storeConfig) throws MessagingException {
super(storeConfig);
ImapStoreSettings settings;
try {
settings = decodeUri(mAccount.getStoreUri());
settings = decodeUri(storeConfig.getStoreUri());
} catch (IllegalArgumentException e) {
throw new MessagingException("Error while decoding store URI", e);
}
@ -502,7 +500,7 @@ public class ImapStore extends Store {
ImapConnection connection = getConnection();
try {
List <? extends Folder > allFolders = listFolders(connection, false);
if (forceListAll || !mAccount.subscribedFoldersOnly()) {
if (forceListAll || !mStoreConfig.subscribedFoldersOnly()) {
return allFolders;
} else {
List<Folder> resultFolders = new LinkedList<Folder>();
@ -570,9 +568,9 @@ public class ImapStore extends Store {
mCombinedPrefix = null;
}
if (folder.equalsIgnoreCase(mAccount.getInboxFolderName())) {
if (folder.equalsIgnoreCase(mStoreConfig.getInboxFolderName())) {
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
* 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;
}
@ -661,17 +659,17 @@ public class ImapStore extends Store {
for (int i = 0, count = attributes.size(); i < count; i++) {
String attribute = attributes.getString(i);
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);
} 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);
} else if (attribute.equals("\\Spam") || attribute.equals("\\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);
} 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);
}
}
@ -777,7 +775,7 @@ public class ImapStore extends Store {
}
class ImapFolder extends Folder {
class ImapFolder extends Folder<ImapMessage> {
private String mName;
protected volatile int mMessageCount = -1;
protected volatile long uidNext = -1L;
@ -789,14 +787,14 @@ public class ImapStore extends Store {
private boolean mInSearch = false;
public ImapFolder(ImapStore nStore, String name) {
super(nStore.getAccount());
super();
store = nStore;
this.mName = name;
}
public String getPrefixedName() throws MessagingException {
String prefixedName = "";
if (!mAccount.getInboxFolderName().equalsIgnoreCase(mName)) {
if (!mStoreConfig.getInboxFolderName().equalsIgnoreCase(mName)) {
ImapConnection connection = null;
synchronized (this) {
if (mConnection == null) {
@ -1293,7 +1291,7 @@ public class ImapStore extends Store {
}
@Override
public Message getMessage(String uid) throws MessagingException {
public ImapMessage getMessage(String uid) throws MessagingException {
return new ImapMessage(uid, this);
}
@ -1351,7 +1349,7 @@ public class ImapStore extends Store {
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
List<Message> messages = new ArrayList<Message>();
@ -1432,7 +1430,7 @@ public class ImapStore extends Store {
}
@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 {
if (messages == null || messages.isEmpty()) {
return;
@ -1468,8 +1466,8 @@ public class ImapStore extends Store {
}
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 (mAccount.getMaximumAutoDownloadMessageSize() > 0) {
fetchFields.add(String.format(Locale.US, "BODY.PEEK[]<0.%d>", mAccount.getMaximumAutoDownloadMessageSize()));
if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) {
fetchFields.add(String.format(Locale.US, "BODY.PEEK[]<0.%d>", mStoreConfig.getMaximumAutoDownloadMessageSize()));
} else {
fetchFields.add("BODY.PEEK[]");
}
@ -1544,7 +1542,7 @@ public class ImapStore extends Store {
}
if (listener != null) {
listener.messageFinished(message, messageNumber, messageMap.size());
listener.messageFinished(imapMessage, messageNumber, messageMap.size());
}
} else {
handleUntaggedResponse(response);
@ -1572,7 +1570,7 @@ public class ImapStore extends Store {
String partId = parts[0];
if ("TEXT".equalsIgnoreCase(partId)) {
fetch = String.format(Locale.US, "BODY.PEEK[TEXT]<0.%d>",
mAccount.getMaximumAutoDownloadMessageSize());
mStoreConfig.getMaximumAutoDownloadMessageSize());
} else {
fetch = String.format("BODY.PEEK[%s]", partId);
}
@ -2194,7 +2192,7 @@ public class ImapStore extends Store {
}
protected String getLogId() {
String id = getAccount().getDescription() + ":" + getName() + "/" + Thread.currentThread().getName();
String id = mStoreConfig.toString() + ":" + getName() + "/" + Thread.currentThread().getName();
if (mConnection != null) {
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)
throws MessagingException {
if (!mAccount.allowRemoteSearch()) {
if (!mStoreConfig.allowRemoteSearch()) {
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);
if (mAccount.isRemoteSearchFullText()) {
if (mStoreConfig.isRemoteSearchFullText()) {
imapQuery += "TEXT " + encodedQry;
} else {
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(),
1024));
@ -2458,7 +2456,7 @@ public class ImapStore extends Store {
mSocket = TrustedSocketFactory.createSocket(mSocket,
mSettings.getHost(), mSettings.getPort(),
mSettings.getClientCertificateAlias());
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT);
mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
mIn = new PeekableInputStream(new BufferedInputStream(mSocket
.getInputStream(), 1024));
mParser = new ImapResponseParser(mIn);
@ -2953,7 +2951,7 @@ public class ImapStore extends Store {
super(store, name);
receiver = nReceiver;
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);
}
@ -2968,7 +2966,7 @@ public class ImapStore extends Store {
if (doneSent.compareAndSet(false, true)) {
ImapConnection conn = mConnection;
if (conn != null) {
conn.setReadTimeout(Store.SOCKET_READ_TIMEOUT);
conn.setReadTimeout(SOCKET_READ_TIMEOUT);
sendContinuation("DONE");
}
@ -3030,7 +3028,7 @@ public class ImapStore extends Store {
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);
storedUntaggedResponses.clear();
processUntaggedResponses(untaggedResponses);
@ -3061,8 +3059,8 @@ public class ImapStore extends Store {
}
}
if (startUid < newUidNext - mAccount.getDisplayCount()) {
startUid = newUidNext - mAccount.getDisplayCount();
if (startUid < newUidNext - mStoreConfig.getDisplayCount()) {
startUid = newUidNext - mStoreConfig.getDisplayCount();
}
if (startUid < 1) {
startUid = 1;
@ -3099,7 +3097,7 @@ public class ImapStore extends Store {
idling.set(true);
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);
idling.set(false);
delayTime.set(NORMAL_DELAY_TIME);
@ -3495,7 +3493,7 @@ public class ImapStore extends Store {
@Override
public int getRefreshInterval() {
return (getAccount().getIdleRefreshMinutes() * 60 * 1000);
return (mStoreConfig.getIdleRefreshMinutes() * 60 * 1000);
}
@Override

View File

@ -3,7 +3,6 @@ package com.fsck.k9.mail.store;
import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.R;
import com.fsck.k9.controller.MessageRetrievalListener;
@ -24,7 +23,6 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
@ -35,7 +33,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
public class Pop3Store extends Store {
public class Pop3Store extends RemoteStore {
public static final String STORE_TYPE = "POP3";
private static final String STLS_COMMAND = "STLS";
@ -209,12 +207,12 @@ public class Pop3Store extends Store {
private boolean mTopNotSupported;
public Pop3Store(Account account) throws MessagingException {
super(account);
public Pop3Store(StoreConfig storeConfig) throws MessagingException {
super(storeConfig);
ServerSettings settings;
try {
settings = decodeUri(mAccount.getStoreUri());
settings = decodeUri(storeConfig.getStoreUri());
} catch (IllegalArgumentException e) {
throw new MessagingException("Error while decoding store URI", e);
}
@ -243,13 +241,13 @@ public class Pop3Store extends Store {
@Override
public List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException {
List<Folder> folders = new LinkedList<Folder>();
folders.add(getFolder(mAccount.getInboxFolderName()));
folders.add(getFolder(mStoreConfig.getInboxFolderName()));
return folders;
}
@Override
public void checkSettings() throws MessagingException {
Pop3Folder folder = new Pop3Folder(mAccount.getInboxFolderName());
Pop3Folder folder = new Pop3Folder(mStoreConfig.getInboxFolderName());
folder.open(Folder.OPEN_MODE_RW);
if (!mCapabilities.uidl) {
/*
@ -272,7 +270,7 @@ public class Pop3Store extends Store {
return false;
}
class Pop3Folder extends Folder {
class Pop3Folder extends Folder<Pop3Message> {
private Socket mSocket;
private InputStream mIn;
private OutputStream mOut;
@ -283,11 +281,11 @@ public class Pop3Store extends Store {
private int mMessageCount;
public Pop3Folder(String name) {
super(Pop3Store.this.mAccount);
super();
this.mName = name;
if (mName.equalsIgnoreCase(mAccount.getInboxFolderName())) {
mName = mAccount.getInboxFolderName();
if (mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName())) {
mName = mStoreConfig.getInboxFolderName();
}
}
@ -297,7 +295,7 @@ public class Pop3Store extends Store {
return;
}
if (!mName.equalsIgnoreCase(mAccount.getInboxFolderName())) {
if (!mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName())) {
throw new MessagingException("Folder does not exist");
}
@ -313,7 +311,7 @@ public class Pop3Store extends Store {
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT);
mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
if (!isOpen()) {
throw new MessagingException("Unable to connect socket");
}
@ -328,7 +326,7 @@ public class Pop3Store extends Store {
mSocket = TrustedSocketFactory.createSocket(mSocket, mHost, mPort,
mClientCertificateAlias);
mSocket.setSoTimeout(Store.SOCKET_READ_TIMEOUT);
mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
if (!isOpen()) {
@ -541,7 +539,7 @@ public class Pop3Store extends Store {
@Override
public boolean exists() throws MessagingException {
return mName.equalsIgnoreCase(mAccount.getInboxFolderName());
return mName.equalsIgnoreCase(mStoreConfig.getInboxFolderName());
}
@Override
@ -559,7 +557,7 @@ public class Pop3Store extends Store {
}
@Override
public Message getMessage(String uid) throws MessagingException {
public Pop3Message getMessage(String uid) throws MessagingException {
Pop3Message message = mUidToMsgMap.get(uid);
if (message == null) {
message = new Pop3Message(uid, this);
@ -761,7 +759,7 @@ public class Pop3Store extends Store {
* @throws MessagingException
*/
@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 {
if (messages == null || messages.isEmpty()) {
return;
@ -805,9 +803,9 @@ public class Pop3Store extends Store {
* To convert the suggested download size we take the size
* divided by the maximum line size (76).
*/
if (mAccount.getMaximumAutoDownloadMessageSize() > 0) {
if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) {
fetchBody(pop3Message,
(mAccount.getMaximumAutoDownloadMessageSize() / 76));
(mStoreConfig.getMaximumAutoDownloadMessageSize() / 76));
} else {
fetchBody(pop3Message, -1);
}
@ -819,7 +817,7 @@ public class Pop3Store extends Store {
pop3Message.setBody(null);
}
if (listener != null && !(fp.contains(FetchProfile.Item.ENVELOPE) && fp.size() == 1)) {
listener.messageFinished(message, i, count);
listener.messageFinished(pop3Message, i, count);
}
} catch (IOException 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 com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.controller.MessageRetrievalListener;
@ -55,7 +54,7 @@ import java.util.zip.GZIPInputStream;
* and email information.
* </pre>
*/
public class WebDavStore extends Store {
public class WebDavStore extends RemoteStore {
public static final String STORE_TYPE = "WebDAV";
// Authentication types
@ -294,12 +293,12 @@ public class WebDavStore extends Store {
private Map<String, WebDavFolder> mFolderList = new HashMap<String, WebDavFolder>();
public WebDavStore(Account account) throws MessagingException {
super(account);
public WebDavStore(StoreConfig storeConfig) throws MessagingException {
super(storeConfig);
WebDavStoreSettings settings;
try {
settings = decodeUri(mAccount.getStoreUri());
settings = decodeUri(storeConfig.getStoreUri());
} catch (IllegalArgumentException 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();
String folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_INBOX_FOLDER));
if (folderName != null) {
mAccount.setAutoExpandFolderName(folderName);
mAccount.setInboxFolderName(folderName);
mStoreConfig.setAutoExpandFolderName(folderName);
mStoreConfig.setInboxFolderName(folderName);
}
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_DRAFTS_FOLDER));
if (folderName != null)
mAccount.setDraftsFolderName(folderName);
mStoreConfig.setDraftsFolderName(folderName);
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_TRASH_FOLDER));
if (folderName != null)
mAccount.setTrashFolderName(folderName);
mStoreConfig.setTrashFolderName(folderName);
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SPAM_FOLDER));
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.
/*
@ -405,7 +404,7 @@ public class WebDavStore extends Store {
folderName = getFolderName(specialFoldersMap.get(DAV_MAIL_SENT_FOLDER));
if (folderName != null)
mAccount.setSentFolderName(folderName);
mStoreConfig.setSentFolderName(folderName);
/**
* Next we get all the folders (including "special" ones)
@ -1196,7 +1195,7 @@ public class WebDavStore extends Store {
@Override
public void sendMessages(List<? extends Message> messages) throws MessagingException {
WebDavFolder tmpFolder = (WebDavStore.WebDavFolder) getFolder(mAccount.getDraftsFolderName());
WebDavFolder tmpFolder = (WebDavStore.WebDavFolder) getFolder(mStoreConfig.getDraftsFolderName());
try {
tmpFolder.open(Folder.OPEN_MODE_RW);
List<? extends Message> retMessages = tmpFolder.appendWebDavMessages(messages);
@ -1216,7 +1215,7 @@ public class WebDavStore extends Store {
/**
* A WebDav Folder
*/
class WebDavFolder extends Folder {
class WebDavFolder extends Folder<WebDavMessage> {
private String mName;
private String mFolderUrl;
private boolean mIsOpen = false;
@ -1229,7 +1228,7 @@ public class WebDavStore extends Store {
}
public WebDavFolder(WebDavStore nStore, String name) {
super(nStore.getAccount());
super();
store = nStore;
this.mName = name;
@ -1397,7 +1396,7 @@ public class WebDavStore extends Store {
}
@Override
public Message getMessage(String uid) throws MessagingException {
public WebDavMessage getMessage(String uid) throws MessagingException {
return new WebDavMessage(uid, this);
}
@ -1498,7 +1497,7 @@ public class WebDavStore extends Store {
}
@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 {
if (messages == null ||
messages.isEmpty()) {
@ -1519,8 +1518,8 @@ public class WebDavStore extends Store {
}
if (fp.contains(FetchProfile.Item.BODY_SANE)) {
if (mAccount.getMaximumAutoDownloadMessageSize() > 0) {
fetchMessages(messages, listener, (mAccount.getMaximumAutoDownloadMessageSize() / 76));
if (mStoreConfig.getMaximumAutoDownloadMessageSize() > 0) {
fetchMessages(messages, listener, (mStoreConfig.getMaximumAutoDownloadMessageSize() / 76));
} else {
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.
*/
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 {
WebDavHttpClient httpclient;
httpclient = getHttpClient();
@ -1594,8 +1593,8 @@ public class WebDavStore extends Store {
if (entity != null) {
InputStream istream = null;
StringBuilder buffer = new StringBuilder();
String tempText = "";
String resultText = "";
String tempText;
String resultText;
BufferedReader reader = null;
int currentLines = 0;

View File

@ -28,6 +28,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.Account.MessageFormat;
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.provider.AttachmentProvider;
public class LocalFolder extends Folder implements Serializable {
public class LocalFolder extends Folder<LocalMessage> implements Serializable {
private static final long serialVersionUID = -1973296520918624767L;
@ -80,22 +81,19 @@ public class LocalFolder extends Folder implements Serializable {
private Integer mLastUid = null;
public LocalFolder(LocalStore localStore, String name) {
super(localStore.getAccount());
super();
this.localStore = localStore;
this.mName = name;
if (this.localStore.getAccount().getInboxFolderName().equals(getName())) {
if (getAccount().getInboxFolderName().equals(getName())) {
mSyncClass = FolderClass.FIRST_CLASS;
mPushClass = FolderClass.FIRST_CLASS;
mInTopGroup = true;
}
}
public LocalFolder(LocalStore localStore, long id) {
super(localStore.getAccount());
super();
this.localStore = localStore;
this.mFolderId = id;
}
@ -104,6 +102,25 @@ public class LocalFolder extends Folder implements Serializable {
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
public void open(final int mode) throws MessagingException {
@ -216,7 +233,7 @@ public class LocalFolder extends Folder implements Serializable {
@Override
public boolean create(FolderType type) throws MessagingException {
return create(type, mAccount.getDisplayCount());
return create(type, getAccount().getDisplayCount());
}
@Override
@ -517,25 +534,25 @@ public class LocalFolder extends Folder implements Serializable {
String id = getPrefId();
// 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");
} else {
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");
} else {
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");
} else {
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");
} else {
editor.putString(id + ".pushMode", mPushClass.name());
@ -597,7 +614,7 @@ public class LocalFolder extends Folder implements Serializable {
}
@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 {
try {
this.localStore.database.execute(false, new DbCallback<Void>() {
@ -614,7 +631,7 @@ public class LocalFolder extends Folder implements Serializable {
try {
cursor = db.rawQuery("SELECT html_content, text_content, mime_type FROM messages "
+ "WHERE id = ?",
new String[] { Long.toString(localMessage.mId) });
new String[] { Long.toString(localMessage.getId()) });
cursor.moveToNext();
String htmlContent = cursor.getString(0);
String textContent = cursor.getString(1);
@ -629,7 +646,7 @@ public class LocalFolder extends Folder implements Serializable {
mp.addBodyPart(bp);
}
if (mAccount.getMessageFormat() != MessageFormat.TEXT) {
if (getAccount().getMessageFormat() != MessageFormat.TEXT) {
if (htmlContent != null) {
TextBody body = new TextBody(htmlContent);
MimeBodyPart bp = new MimeBodyPart(body, "text/html");
@ -700,7 +717,7 @@ public class LocalFolder extends Folder implements Serializable {
"content_disposition"
},
"message_id = ?",
new String[] { Long.toString(localMessage.mId) },
new String[] { Long.toString(localMessage.getId()) },
null,
null,
null);
@ -1439,12 +1456,12 @@ public class LocalFolder extends Folder implements Serializable {
message.isSet(Flag.FLAGGED) ? 1 : 0,
message.isSet(Flag.ANSWERED) ? 1 : 0,
message.isSet(Flag.FORWARDED) ? 1 : 0,
message.mId
message.getId()
});
for (int i = 0, count = attachments.size(); i < count; i++) {
Part attachment = attachments.get(i);
saveAttachment(message.mId, attachment, false);
saveAttachment(message.getId(), attachment, false);
}
saveHeaders(message.getId(), message);
} catch (Exception e) {
@ -1637,7 +1654,7 @@ public class LocalFolder extends Folder implements Serializable {
File attachmentFile = new File(attachmentDirectory, Long.toString(attachmentId));
tempAttachmentFile.renameTo(attachmentFile);
contentUri = AttachmentProvider.getAttachmentUri(
mAccount,
getAccount(),
attachmentId);
if (MimeUtil.isMessage(attachment.getMimeType())) {
attachment.setBody(new LocalAttachmentMessageBody(
@ -1712,7 +1729,7 @@ public class LocalFolder extends Folder implements Serializable {
@Override
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
db.update("messages", cv, "id = ?", new String[]
{ Long.toString(message.mId) });
{ Long.toString(message.getId()) });
return null;
}
});
@ -1829,7 +1846,7 @@ public class LocalFolder extends Folder implements Serializable {
setPushState(null);
setLastPush(0);
setLastChecked(0);
setVisibleLimit(mAccount.getDisplayCount());
setVisibleLimit(getAccount().getDisplayCount());
}
@Override
@ -1878,7 +1895,7 @@ public class LocalFolder extends Folder implements Serializable {
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
Cursor attachmentsCursor = null;
try {
String accountUuid = mAccount.getUuid();
String accountUuid = getUuid();
Context context = LocalFolder.this.localStore.mApplication;
// 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
String[] inReplyToArray = message.getHeader("In-Reply-To");
String inReplyTo = null;
String inReplyTo;
if (inReplyToArray != null && inReplyToArray.length > 0) {
inReplyTo = Utility.extractMessageId(inReplyToArray[0]);
if (inReplyTo != null) {
@ -2194,4 +2211,8 @@ public class LocalFolder extends Folder implements Serializable {
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.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.activity.MessageReference;
import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Folder;
@ -28,7 +30,7 @@ public class LocalMessage extends MimeMessage {
private final LocalStore localStore;
long mId;
private long mId;
private int mAttachmentCount;
private String mSubject;
@ -557,4 +559,43 @@ public class LocalMessage extends MimeMessage {
public long getRootId() {
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.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import android.app.Application;
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.MessagingException;
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.WrappedException;
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 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
* in the correct order.
@ -138,6 +154,7 @@ public class LocalStore extends Store implements Serializable {
LockableDatabase database;
private ContentResolver mContentResolver;
private final Account mAccount;
/**
* 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)}
*/
public LocalStore(final Account account, final Application application) throws MessagingException {
super(account);
mAccount = account;
database = new LockableDatabase(application, account.getUuid(), new StoreSchemaDefinition(this));
mApplication = application;
@ -158,10 +175,73 @@ public class LocalStore extends Store implements Serializable {
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 {
database.switchProvider(newStorageProviderId);
}
protected Account getAccount() {
return mAccount;
}
protected SharedPreferences 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.SmtpDataStuffing;
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.net.ssl.TrustedSocketFactory;
@ -183,7 +184,7 @@ public class SmtpTransport extends Transport {
private boolean m8bitEncodingAllowed;
private int mLargestAcceptableMessage;
public SmtpTransport(Account account) throws MessagingException {
public SmtpTransport(StoreConfig account) throws MessagingException {
ServerSettings settings;
try {
settings = decodeUri(account.getTransportUri());

View File

@ -3,12 +3,12 @@ package com.fsck.k9.mail.transport;
import android.util.Log;
import com.fsck.k9.Account;
import com.fsck.k9.K9;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.store.StoreConfig;
import com.fsck.k9.mail.store.WebDavStore;
import java.util.Collections;
@ -43,12 +43,8 @@ public class WebDavTransport extends Transport {
private WebDavStore store;
public WebDavTransport(Account account) throws MessagingException {
if (account.getRemoteStore() instanceof WebDavStore) {
store = (WebDavStore) account.getRemoteStore();
} else {
store = new WebDavStore(account);
}
public WebDavTransport(StoreConfig configInterface) throws MessagingException {
store = new WebDavStore(configInterface);
if (K9.DEBUG)
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.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.ServerSettings;
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.SettingsDescription;
@ -223,7 +223,7 @@ public class SettingsExporter {
// Write incoming server settings
ServerSettings incoming = Store.decodeStoreUri(account.getStoreUri());
ServerSettings incoming = RemoteStore.decodeStoreUri(account.getStoreUri());
serializer.startTag(null, INCOMING_SERVER_ELEMENT);
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.ConnectionSecurity;
import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.store.RemoteStore;
import com.fsck.k9.mail.store.WebDavStore;
import com.fsck.k9.preferences.Settings.InvalidSettingValueException;
@ -376,7 +376,7 @@ public class SettingsImporter {
// Write incoming server settings (storeUri)
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));
// 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;
try {
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) {
Log.e(K9.LOG_TAG, "Unable to retrieve attachment info from local store for ID: " + id, e);
return null;
@ -269,7 +269,7 @@ public class AttachmentProvider extends ContentProvider {
final Account account = Preferences.getPreferences(getContext()).getAccount(dbName);
try {
final LocalStore localStore = LocalStore.getLocalInstance(account, K9.app);
final LocalStore localStore = LocalStore.getInstance(account, K9.app);
AttachmentInfo attachmentInfo = localStore.getAttachmentInfo(id);
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.Message;
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.LocalStore;
import com.fsck.k9.search.SearchAccount;
@ -200,9 +201,9 @@ public class MessageProvider extends ContentProvider {
public static class DeleteUriExtractor implements FieldExtractor<MessageInfoHolder, String> {
@Override
public String getField(final MessageInfoHolder source) {
final Message message = source.message;
final LocalMessage message = source.message;
return CONTENT_URI + "/delete_message/"
+ message.getFolder().getAccount().getAccountNumber() + "/"
+ message.getAccount().getAccountNumber() + "/"
+ message.getFolder().getName() + "/" + message.getUid();
}
}
@ -221,21 +222,21 @@ public class MessageProvider extends ContentProvider {
public static class AccountExtractor implements FieldExtractor<MessageInfoHolder, String> {
@Override
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> {
@Override
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> {
@Override
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
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
final MessageHelper helper = mMessageHelper;
final List<MessageInfoHolder> holders = mHolders;
final Context context = getContext();
for (final Message message : messages) {
for (final LocalMessage message : messages) {
final MessageInfoHolder messageInfoHolder = new MessageInfoHolder();
final Folder messageFolder = message.getFolder();
final Account messageAccount = messageFolder.getAccount();
final Account messageAccount = messageInfoHolder.message.getAccount();
helper.populate(messageInfoHolder, message, new FolderInfoHolder(context,
messageFolder, messageAccount), messageAccount);
@ -1038,9 +1039,9 @@ public class MessageProvider extends ContentProvider {
}
// get localstore parameter
Message msg = null;
LocalMessage msg = null;
try {
Folder lf = LocalStore.getLocalInstance(myAccount, K9.app).getFolder(folderName);
LocalFolder lf = LocalStore.getInstance(myAccount, K9.app).getFolder(folderName);
int msgCount = lf.getMessageCount();
if (K9.DEBUG) {
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.MessageReference;
import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.store.local.LocalMessage;
import android.app.PendingIntent;
import android.content.Context;
@ -89,10 +89,10 @@ public class NotificationActionService extends CoreService {
List<MessageReference> refs =
intent.getParcelableArrayListExtra(EXTRA_MESSAGE_LIST);
List<Message> messages = new ArrayList<Message>();
List<LocalMessage> messages = new ArrayList<LocalMessage>();
for (MessageReference ref : refs) {
Message m = ref.restoreToLocalMessage(this);
LocalMessage m = ref.restoreToLocalMessage(this);
if (m != null) {
messages.add(m);
}
@ -106,7 +106,7 @@ public class NotificationActionService extends CoreService {
MessageReference ref = (MessageReference) intent.getParcelableExtra(EXTRA_MESSAGE);
Message message = ref.restoreToLocalMessage(this);
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);
startActivity(i);
} else {

View File

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