mirror of
https://github.com/moparisthebest/k-9
synced 2024-11-30 13:12:25 -05:00
Merge pull request #3 from frommeyerc/merge-upstream
Extract inner classes from LocalStore
This commit is contained in:
commit
bd839a995f
@ -31,9 +31,9 @@ import com.fsck.k9.mail.Address;
|
|||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Store;
|
import com.fsck.k9.mail.Store;
|
||||||
import com.fsck.k9.mail.Folder.FolderClass;
|
import com.fsck.k9.mail.Folder.FolderClass;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
|
||||||
import com.fsck.k9.mail.store.StorageManager;
|
import com.fsck.k9.mail.store.StorageManager;
|
||||||
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
|
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
|
||||||
|
import com.fsck.k9.mail.store.local.LocalStore;
|
||||||
import com.fsck.k9.provider.EmailProvider;
|
import com.fsck.k9.provider.EmailProvider;
|
||||||
import com.fsck.k9.provider.EmailProvider.StatsColumns;
|
import com.fsck.k9.provider.EmailProvider.StatsColumns;
|
||||||
import com.fsck.k9.search.ConditionsTreeNode;
|
import com.fsck.k9.search.ConditionsTreeNode;
|
||||||
|
@ -37,7 +37,7 @@ import com.fsck.k9.mail.Address;
|
|||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
import com.fsck.k9.mail.store.local.LocalStore;
|
||||||
import com.fsck.k9.provider.UnreadWidgetProvider;
|
import com.fsck.k9.provider.UnreadWidgetProvider;
|
||||||
import com.fsck.k9.security.LocalKeyStore;
|
import com.fsck.k9.security.LocalKeyStore;
|
||||||
import com.fsck.k9.service.BootReceiver;
|
import com.fsck.k9.service.BootReceiver;
|
||||||
|
@ -56,7 +56,7 @@ import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock;
|
|||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||||
import com.fsck.k9.search.LocalSearch;
|
import com.fsck.k9.search.LocalSearch;
|
||||||
import com.fsck.k9.search.SearchSpecification.Attribute;
|
import com.fsck.k9.search.SearchSpecification.Attribute;
|
||||||
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
||||||
|
@ -107,9 +107,9 @@ import com.fsck.k9.mail.internet.MimeMultipart;
|
|||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
import com.fsck.k9.mail.internet.TextBodyBuilder;
|
import com.fsck.k9.mail.internet.TextBodyBuilder;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody;
|
import com.fsck.k9.mail.store.local.LocalAttachmentBody;
|
||||||
import com.fsck.k9.mail.store.LocalStore.TempFileBody;
|
import com.fsck.k9.mail.store.local.TempFileBody;
|
||||||
import com.fsck.k9.mail.store.LocalStore.TempFileMessageBody;
|
import com.fsck.k9.mail.store.local.TempFileMessageBody;
|
||||||
import com.fsck.k9.view.MessageWebView;
|
import com.fsck.k9.view.MessageWebView;
|
||||||
|
|
||||||
import org.apache.james.mime4j.codec.EncoderUtil;
|
import org.apache.james.mime4j.codec.EncoderUtil;
|
||||||
|
@ -30,7 +30,7 @@ import android.widget.TextView;
|
|||||||
* <li>{@link #actionUpgradeDatabases(Context, Intent)} will call {@link K9#areDatabasesUpToDate()}
|
* <li>{@link #actionUpgradeDatabases(Context, Intent)} will call {@link K9#areDatabasesUpToDate()}
|
||||||
* to check if we already know whether the databases have been upgraded.</li>
|
* to check if we already know whether the databases have been upgraded.</li>
|
||||||
* <li>{@link K9#areDatabasesUpToDate()} will compare the last known database version stored in a
|
* <li>{@link K9#areDatabasesUpToDate()} will compare the last known database version stored in a
|
||||||
* {@link SharedPreferences} file to {@link com.fsck.k9.mail.store.LocalStore#DB_VERSION}. This
|
* {@link SharedPreferences} file to {@link com.fsck.k9.mail.store.local.LocalStore#DB_VERSION}. This
|
||||||
* is done as an optimization because it's faster than opening all of the accounts' databases
|
* is done as an optimization because it's faster than opening all of the accounts' databases
|
||||||
* one by one.</li>
|
* one by one.</li>
|
||||||
* <li>If there was an error reading the cached database version or if it shows the databases need
|
* <li>If there was an error reading the cached database version or if it shows the databases need
|
||||||
|
@ -41,7 +41,7 @@ import com.fsck.k9.activity.ManageIdentities;
|
|||||||
import com.fsck.k9.crypto.Apg;
|
import com.fsck.k9.crypto.Apg;
|
||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.Store;
|
import com.fsck.k9.mail.Store;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.StorageManager;
|
import com.fsck.k9.mail.store.StorageManager;
|
||||||
import com.fsck.k9.service.MailService;
|
import com.fsck.k9.service.MailService;
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@ import com.fsck.k9.mail.Folder.FolderClass;
|
|||||||
|
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Store;
|
import com.fsck.k9.mail.Store;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
import com.fsck.k9.mail.store.local.LocalStore;
|
||||||
import com.fsck.k9.service.MailService;
|
import com.fsck.k9.service.MailService;
|
||||||
|
|
||||||
public class FolderSettings extends K9PreferenceActivity {
|
public class FolderSettings extends K9PreferenceActivity {
|
||||||
|
@ -11,8 +11,8 @@ import android.support.v4.content.LocalBroadcastManager;
|
|||||||
|
|
||||||
import com.fsck.k9.fragment.MessageListFragment;
|
import com.fsck.k9.fragment.MessageListFragment;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||||
import com.fsck.k9.provider.EmailProvider;
|
import com.fsck.k9.provider.EmailProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,10 +79,10 @@ import com.fsck.k9.mail.Transport;
|
|||||||
import com.fsck.k9.mail.internet.MimeMessage;
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.internet.TextBody;
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
import com.fsck.k9.mail.store.local.LocalStore;
|
||||||
import com.fsck.k9.mail.store.LocalStore.PendingCommand;
|
import com.fsck.k9.mail.store.local.LocalStore.PendingCommand;
|
||||||
import com.fsck.k9.mail.store.Pop3Store;
|
import com.fsck.k9.mail.store.Pop3Store;
|
||||||
import com.fsck.k9.mail.store.UnavailableAccountException;
|
import com.fsck.k9.mail.store.UnavailableAccountException;
|
||||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||||
|
@ -11,8 +11,8 @@ import com.fsck.k9.mail.Folder;
|
|||||||
|
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.PushReceiver;
|
import com.fsck.k9.mail.PushReceiver;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
import com.fsck.k9.mail.store.local.LocalStore;
|
||||||
import com.fsck.k9.service.SleepService;
|
import com.fsck.k9.service.SleepService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -88,8 +88,8 @@ import com.fsck.k9.mail.Flag;
|
|||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
import com.fsck.k9.mail.store.local.LocalStore;
|
||||||
import com.fsck.k9.provider.EmailProvider;
|
import com.fsck.k9.provider.EmailProvider;
|
||||||
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
||||||
import com.fsck.k9.provider.EmailProvider.SpecialColumns;
|
import com.fsck.k9.provider.EmailProvider.SpecialColumns;
|
||||||
|
@ -39,7 +39,7 @@ import com.fsck.k9.mail.Flag;
|
|||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||||
import com.fsck.k9.view.AttachmentView;
|
import com.fsck.k9.view.AttachmentView;
|
||||||
import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback;
|
import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback;
|
||||||
import com.fsck.k9.view.MessageHeader;
|
import com.fsck.k9.view.MessageHeader;
|
||||||
|
@ -12,9 +12,9 @@ import android.util.Log;
|
|||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.K9;
|
import com.fsck.k9.K9;
|
||||||
import com.fsck.k9.mail.store.ImapStore;
|
import com.fsck.k9.mail.store.ImapStore;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
|
||||||
import com.fsck.k9.mail.store.Pop3Store;
|
import com.fsck.k9.mail.store.Pop3Store;
|
||||||
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
|
import com.fsck.k9.mail.store.StorageManager.StorageProvider;
|
||||||
|
import com.fsck.k9.mail.store.local.LocalStore;
|
||||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||||
import com.fsck.k9.mail.store.WebDavStore;
|
import com.fsck.k9.mail.store.WebDavStore;
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -376,24 +376,12 @@ public class LockableDatabase {
|
|||||||
try {
|
try {
|
||||||
final File databaseFile = prepareStorage(mStorageProviderId);
|
final File databaseFile = prepareStorage(mStorageProviderId);
|
||||||
try {
|
try {
|
||||||
if (StorageManager.InternalStorageProvider.ID.equals(mStorageProviderId)) {
|
doOpenOrCreateDb(application, databaseFile);
|
||||||
// internal storage
|
|
||||||
mDb = application.openOrCreateDatabase(databaseFile.getName(), Context.MODE_PRIVATE, null);
|
|
||||||
} else {
|
|
||||||
// external storage
|
|
||||||
mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, null);
|
|
||||||
}
|
|
||||||
} catch (SQLiteException e) {
|
} catch (SQLiteException e) {
|
||||||
// try to gracefully handle DB corruption - see issue 2537
|
// try to gracefully handle DB corruption - see issue 2537
|
||||||
Log.w(K9.LOG_TAG, "Unable to open DB " + databaseFile + " - removing file and retrying", e);
|
Log.w(K9.LOG_TAG, "Unable to open DB " + databaseFile + " - removing file and retrying", e);
|
||||||
databaseFile.delete();
|
databaseFile.delete();
|
||||||
if (StorageManager.InternalStorageProvider.ID.equals(mStorageProviderId)) {
|
doOpenOrCreateDb(application, databaseFile);
|
||||||
// internal storage
|
|
||||||
mDb = application.openOrCreateDatabase(databaseFile.getName(), Context.MODE_PRIVATE, null);
|
|
||||||
} else {
|
|
||||||
// external storage
|
|
||||||
mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (mDb.getVersion() != mSchemaDefinition.getVersion()) {
|
if (mDb.getVersion() != mSchemaDefinition.getVersion()) {
|
||||||
mSchemaDefinition.doDbUpgrade(mDb);
|
mSchemaDefinition.doDbUpgrade(mDb);
|
||||||
@ -403,6 +391,17 @@ public class LockableDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void doOpenOrCreateDb(final Application application, final File databaseFile) {
|
||||||
|
if (StorageManager.InternalStorageProvider.ID.equals(mStorageProviderId)) {
|
||||||
|
// internal storage
|
||||||
|
mDb = application.openOrCreateDatabase(databaseFile.getName(), Context.MODE_PRIVATE,
|
||||||
|
null);
|
||||||
|
} else {
|
||||||
|
// external storage
|
||||||
|
mDb = SQLiteDatabase.openOrCreateDatabase(databaseFile, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param providerId
|
* @param providerId
|
||||||
* Never <code>null</code>.
|
* Never <code>null</code>.
|
||||||
@ -412,10 +411,8 @@ public class LockableDatabase {
|
|||||||
protected File prepareStorage(final String providerId) throws UnavailableStorageException {
|
protected File prepareStorage(final String providerId) throws UnavailableStorageException {
|
||||||
final StorageManager storageManager = getStorageManager();
|
final StorageManager storageManager = getStorageManager();
|
||||||
|
|
||||||
final File databaseFile;
|
final File databaseFile = storageManager.getDatabase(uUid, providerId);
|
||||||
final File databaseParentDir;
|
final File databaseParentDir = databaseFile.getParentFile();
|
||||||
databaseFile = storageManager.getDatabase(uUid, providerId);
|
|
||||||
databaseParentDir = databaseFile.getParentFile();
|
|
||||||
if (databaseParentDir.isFile()) {
|
if (databaseParentDir.isFile()) {
|
||||||
// should be safe to unconditionally delete clashing file: user is not supposed to mess with our directory
|
// should be safe to unconditionally delete clashing file: user is not supposed to mess with our directory
|
||||||
databaseParentDir.delete();
|
databaseParentDir.delete();
|
||||||
@ -428,11 +425,8 @@ public class LockableDatabase {
|
|||||||
Utility.touchFile(databaseParentDir, ".nomedia");
|
Utility.touchFile(databaseParentDir, ".nomedia");
|
||||||
}
|
}
|
||||||
|
|
||||||
final File attachmentDir;
|
final File attachmentDir = storageManager.getAttachmentDirectory(uUid, providerId);
|
||||||
final File attachmentParentDir;
|
final File attachmentParentDir = attachmentDir.getParentFile();
|
||||||
attachmentDir = storageManager
|
|
||||||
.getAttachmentDirectory(uUid, providerId);
|
|
||||||
attachmentParentDir = attachmentDir.getParentFile();
|
|
||||||
if (!attachmentParentDir.exists()) {
|
if (!attachmentParentDir.exists()) {
|
||||||
attachmentParentDir.mkdirs();
|
attachmentParentDir.mkdirs();
|
||||||
Utility.touchFile(attachmentParentDir, ".nomedia");
|
Utility.touchFile(attachmentParentDir, ".nomedia");
|
||||||
@ -467,7 +461,8 @@ public class LockableDatabase {
|
|||||||
try {
|
try {
|
||||||
mDb.close();
|
mDb.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
if (K9.DEBUG)
|
||||||
|
Log.d(K9.LOG_TAG, "Exception caught in DB close: " + e.getMessage());
|
||||||
}
|
}
|
||||||
final StorageManager storageManager = getStorageManager();
|
final StorageManager storageManager = getStorageManager();
|
||||||
try {
|
try {
|
||||||
@ -482,6 +477,8 @@ public class LockableDatabase {
|
|||||||
attachmentDirectory.delete();
|
attachmentDirectory.delete();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
if (K9.DEBUG)
|
||||||
|
Log.d(K9.LOG_TAG, "Exception caught in clearing attachments: " + e.getMessage());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
deleteDatabase(storageManager.getDatabase(uUid, mStorageProviderId));
|
deleteDatabase(storageManager.getDatabase(uUid, mStorageProviderId));
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
|
|
||||||
|
public class AttachmentMessageBodyUtil {
|
||||||
|
public static void writeTo(BinaryAttachmentBody body, OutputStream out) throws IOException,
|
||||||
|
MessagingException {
|
||||||
|
InputStream in = body.getInputStream();
|
||||||
|
try {
|
||||||
|
if (MimeUtil.ENC_7BIT.equalsIgnoreCase(body.getEncoding())) {
|
||||||
|
/*
|
||||||
|
* If we knew the message was already 7bit clean, then it
|
||||||
|
* could be sent along without processing. But since we
|
||||||
|
* don't know, we recursively parse it.
|
||||||
|
*/
|
||||||
|
MimeMessage message = new MimeMessage(in, true);
|
||||||
|
message.setUsing7bitTransport();
|
||||||
|
message.writeTo(out);
|
||||||
|
} else {
|
||||||
|
IOUtils.copy(in, out);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
src/com/fsck/k9/mail/store/local/BinaryAttachmentBody.java
Normal file
54
src/com/fsck/k9/mail/store/local/BinaryAttachmentBody.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.james.mime4j.codec.QuotedPrintableOutputStream;
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.Body;
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
import com.fsck.k9.mail.filter.Base64OutputStream;
|
||||||
|
|
||||||
|
public abstract class BinaryAttachmentBody implements Body {
|
||||||
|
protected String mEncoding;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract InputStream getInputStream() throws MessagingException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||||
|
InputStream in = getInputStream();
|
||||||
|
try {
|
||||||
|
boolean closeStream = false;
|
||||||
|
if (MimeUtil.isBase64Encoding(mEncoding)) {
|
||||||
|
out = new Base64OutputStream(out);
|
||||||
|
closeStream = true;
|
||||||
|
} else if (MimeUtil.isQuotedPrintableEncoded(mEncoding)){
|
||||||
|
out = new QuotedPrintableOutputStream(out, false);
|
||||||
|
closeStream = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
IOUtils.copy(in, out);
|
||||||
|
} finally {
|
||||||
|
if (closeStream) {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEncoding(String encoding) throws MessagingException {
|
||||||
|
mEncoding = encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEncoding() {
|
||||||
|
return mEncoding;
|
||||||
|
}
|
||||||
|
}
|
37
src/com/fsck/k9/mail/store/local/LocalAttachmentBody.java
Normal file
37
src/com/fsck/k9/mail/store/local/LocalAttachmentBody.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
|
||||||
|
public class LocalAttachmentBody extends BinaryAttachmentBody {
|
||||||
|
private Application mApplication;
|
||||||
|
private Uri mUri;
|
||||||
|
|
||||||
|
public LocalAttachmentBody(Uri uri, Application application) {
|
||||||
|
mApplication = application;
|
||||||
|
mUri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws MessagingException {
|
||||||
|
try {
|
||||||
|
return mApplication.getContentResolver().openInputStream(mUri);
|
||||||
|
} catch (FileNotFoundException fnfe) {
|
||||||
|
/*
|
||||||
|
* Since it's completely normal for us to try to serve up attachments that
|
||||||
|
* have been blown away, we just return an empty stream.
|
||||||
|
*/
|
||||||
|
return new ByteArrayInputStream(LocalStore.EMPTY_BYTE_ARRAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uri getContentUri() {
|
||||||
|
return mUri;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.Body;
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
import com.fsck.k9.mail.internet.MimeBodyPart;
|
||||||
|
|
||||||
|
public class LocalAttachmentBodyPart extends MimeBodyPart {
|
||||||
|
private long mAttachmentId = -1;
|
||||||
|
|
||||||
|
public LocalAttachmentBodyPart(Body body, long attachmentId) throws MessagingException {
|
||||||
|
super(body);
|
||||||
|
mAttachmentId = attachmentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the local attachment id of this body, or -1 if it is not stored.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public long getAttachmentId() {
|
||||||
|
return mAttachmentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachmentId(long attachmentId) {
|
||||||
|
mAttachmentId = attachmentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "" + mAttachmentId;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.CompositeBody;
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link LocalAttachmentBody} extension containing a message/rfc822 type body
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LocalAttachmentMessageBody extends LocalAttachmentBody implements CompositeBody {
|
||||||
|
|
||||||
|
public LocalAttachmentMessageBody(Uri uri, Application application) {
|
||||||
|
super(uri, application);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||||
|
AttachmentMessageBodyUtil.writeTo(this, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsing7bitTransport() throws MessagingException {
|
||||||
|
/*
|
||||||
|
* There's nothing to recurse into here, so there's nothing to do.
|
||||||
|
* The enclosing BodyPart already called setEncoding(MimeUtil.ENC_7BIT). Once
|
||||||
|
* writeTo() is called, the file with the rfc822 body will be opened
|
||||||
|
* for reading and will then be recursed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEncoding(String encoding) throws MessagingException {
|
||||||
|
if (!MimeUtil.ENC_7BIT.equalsIgnoreCase(encoding)
|
||||||
|
&& !MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) {
|
||||||
|
throw new MessagingException(
|
||||||
|
"Incompatible content-transfer-encoding applied to a CompositeBody");
|
||||||
|
}
|
||||||
|
mEncoding = encoding;
|
||||||
|
}
|
||||||
|
}
|
2197
src/com/fsck/k9/mail/store/local/LocalFolder.java
Normal file
2197
src/com/fsck/k9/mail/store/local/LocalFolder.java
Normal file
File diff suppressed because it is too large
Load Diff
559
src/com/fsck/k9/mail/store/local/LocalMessage.java
Normal file
559
src/com/fsck/k9/mail/store/local/LocalMessage.java
Normal file
@ -0,0 +1,559 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fsck.k9.K9;
|
||||||
|
import com.fsck.k9.mail.Address;
|
||||||
|
import com.fsck.k9.mail.Flag;
|
||||||
|
import com.fsck.k9.mail.Folder;
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
import com.fsck.k9.mail.Part;
|
||||||
|
import com.fsck.k9.mail.internet.MimeMessage;
|
||||||
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
|
import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||||
|
import com.fsck.k9.mail.store.LockableDatabase.DbCallback;
|
||||||
|
import com.fsck.k9.mail.store.LockableDatabase.WrappedException;
|
||||||
|
|
||||||
|
public class LocalMessage extends MimeMessage {
|
||||||
|
|
||||||
|
private final LocalStore localStore;
|
||||||
|
|
||||||
|
long mId;
|
||||||
|
private int mAttachmentCount;
|
||||||
|
private String mSubject;
|
||||||
|
|
||||||
|
private String mPreview = "";
|
||||||
|
|
||||||
|
private boolean mHeadersLoaded = false;
|
||||||
|
private boolean mMessageDirty = false;
|
||||||
|
|
||||||
|
private long mThreadId;
|
||||||
|
private long mRootId;
|
||||||
|
|
||||||
|
public LocalMessage(LocalStore localStore) {
|
||||||
|
this.localStore = localStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalMessage(LocalStore localStore, String uid, Folder folder) {
|
||||||
|
this.localStore = localStore;
|
||||||
|
this.mUid = uid;
|
||||||
|
this.mFolder = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void populateFromGetMessageCursor(Cursor cursor)
|
||||||
|
throws MessagingException {
|
||||||
|
final String subject = cursor.getString(0);
|
||||||
|
this.setSubject(subject == null ? "" : subject);
|
||||||
|
|
||||||
|
Address[] from = Address.unpack(cursor.getString(1));
|
||||||
|
if (from.length > 0) {
|
||||||
|
this.setFrom(from[0]);
|
||||||
|
}
|
||||||
|
this.setInternalSentDate(new Date(cursor.getLong(2)));
|
||||||
|
this.setUid(cursor.getString(3));
|
||||||
|
String flagList = cursor.getString(4);
|
||||||
|
if (flagList != null && flagList.length() > 0) {
|
||||||
|
String[] flags = flagList.split(",");
|
||||||
|
|
||||||
|
for (String flag : flags) {
|
||||||
|
try {
|
||||||
|
this.setFlagInternal(Flag.valueOf(flag), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (Exception e) {
|
||||||
|
if (!"X_BAD_FLAG".equals(flag)) {
|
||||||
|
Log.w(K9.LOG_TAG, "Unable to parse flag " + flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.mId = cursor.getLong(5);
|
||||||
|
this.setRecipients(RecipientType.TO, Address.unpack(cursor.getString(6)));
|
||||||
|
this.setRecipients(RecipientType.CC, Address.unpack(cursor.getString(7)));
|
||||||
|
this.setRecipients(RecipientType.BCC, Address.unpack(cursor.getString(8)));
|
||||||
|
this.setReplyTo(Address.unpack(cursor.getString(9)));
|
||||||
|
|
||||||
|
this.mAttachmentCount = cursor.getInt(10);
|
||||||
|
this.setInternalDate(new Date(cursor.getLong(11)));
|
||||||
|
this.setMessageId(cursor.getString(12));
|
||||||
|
|
||||||
|
final String preview = cursor.getString(14);
|
||||||
|
mPreview = (preview == null ? "" : preview);
|
||||||
|
|
||||||
|
if (this.mFolder == null) {
|
||||||
|
LocalFolder f = new LocalFolder(this.localStore, cursor.getInt(13));
|
||||||
|
f.open(LocalFolder.OPEN_MODE_RW);
|
||||||
|
this.mFolder = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
mThreadId = (cursor.isNull(15)) ? -1 : cursor.getLong(15);
|
||||||
|
mRootId = (cursor.isNull(16)) ? -1 : cursor.getLong(16);
|
||||||
|
|
||||||
|
boolean deleted = (cursor.getInt(17) == 1);
|
||||||
|
boolean read = (cursor.getInt(18) == 1);
|
||||||
|
boolean flagged = (cursor.getInt(19) == 1);
|
||||||
|
boolean answered = (cursor.getInt(20) == 1);
|
||||||
|
boolean forwarded = (cursor.getInt(21) == 1);
|
||||||
|
|
||||||
|
setFlagInternal(Flag.DELETED, deleted);
|
||||||
|
setFlagInternal(Flag.SEEN, read);
|
||||||
|
setFlagInternal(Flag.FLAGGED, flagged);
|
||||||
|
setFlagInternal(Flag.ANSWERED, answered);
|
||||||
|
setFlagInternal(Flag.FORWARDED, forwarded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the message text for display. This always returns an HTML-ified version of the
|
||||||
|
* message, even if it was originally a text-only message.
|
||||||
|
* @return HTML version of message for display purposes or null.
|
||||||
|
* @throws MessagingException
|
||||||
|
*/
|
||||||
|
public String getTextForDisplay() throws MessagingException {
|
||||||
|
String text = null; // First try and fetch an HTML part.
|
||||||
|
Part part = MimeUtility.findFirstPartByMimeType(this, "text/html");
|
||||||
|
if (part == null) {
|
||||||
|
// If that fails, try and get a text part.
|
||||||
|
part = MimeUtility.findFirstPartByMimeType(this, "text/plain");
|
||||||
|
if (part != null && part.getBody() instanceof LocalTextBody) {
|
||||||
|
text = ((LocalTextBody) part.getBody()).getBodyForDisplay();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We successfully found an HTML part; do the necessary character set decoding.
|
||||||
|
text = MimeUtility.getTextFromPart(part);
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Custom version of writeTo that updates the MIME message based on localMessage
|
||||||
|
* changes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||||
|
if (mMessageDirty) buildMimeRepresentation();
|
||||||
|
super.writeTo(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void buildMimeRepresentation() throws MessagingException {
|
||||||
|
if (!mMessageDirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setSubject(mSubject);
|
||||||
|
if (this.mFrom != null && this.mFrom.length > 0) {
|
||||||
|
super.setFrom(this.mFrom[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setReplyTo(mReplyTo);
|
||||||
|
super.setSentDate(this.getSentDate());
|
||||||
|
super.setRecipients(RecipientType.TO, mTo);
|
||||||
|
super.setRecipients(RecipientType.CC, mCc);
|
||||||
|
super.setRecipients(RecipientType.BCC, mBcc);
|
||||||
|
if (mMessageId != null) super.setMessageId(mMessageId);
|
||||||
|
|
||||||
|
mMessageDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPreview() {
|
||||||
|
return mPreview;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSubject() {
|
||||||
|
return mSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSubject(String subject) throws MessagingException {
|
||||||
|
mSubject = subject;
|
||||||
|
mMessageDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMessageId(String messageId) {
|
||||||
|
mMessageId = messageId;
|
||||||
|
mMessageDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAttachments() {
|
||||||
|
return (mAttachmentCount > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAttachmentCount() {
|
||||||
|
return mAttachmentCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFrom(Address from) throws MessagingException {
|
||||||
|
this.mFrom = new Address[] { from };
|
||||||
|
mMessageDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReplyTo(Address[] replyTo) throws MessagingException {
|
||||||
|
if (replyTo == null || replyTo.length == 0) {
|
||||||
|
mReplyTo = null;
|
||||||
|
} else {
|
||||||
|
mReplyTo = replyTo;
|
||||||
|
}
|
||||||
|
mMessageDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For performance reasons, we add headers instead of setting them (see super implementation)
|
||||||
|
* which removes (expensive) them before adding them
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setRecipients(RecipientType type, Address[] addresses) throws MessagingException {
|
||||||
|
if (type == RecipientType.TO) {
|
||||||
|
if (addresses == null || addresses.length == 0) {
|
||||||
|
this.mTo = null;
|
||||||
|
} else {
|
||||||
|
this.mTo = addresses;
|
||||||
|
}
|
||||||
|
} else if (type == RecipientType.CC) {
|
||||||
|
if (addresses == null || addresses.length == 0) {
|
||||||
|
this.mCc = null;
|
||||||
|
} else {
|
||||||
|
this.mCc = addresses;
|
||||||
|
}
|
||||||
|
} else if (type == RecipientType.BCC) {
|
||||||
|
if (addresses == null || addresses.length == 0) {
|
||||||
|
this.mBcc = null;
|
||||||
|
} else {
|
||||||
|
this.mBcc = addresses;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new MessagingException("Unrecognized recipient type.");
|
||||||
|
}
|
||||||
|
mMessageDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlagInternal(Flag flag, boolean set) throws MessagingException {
|
||||||
|
super.setFlag(flag, set);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return mId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFlag(final Flag flag, final boolean set) throws MessagingException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.localStore.database.execute(true, new DbCallback<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException, UnavailableStorageException {
|
||||||
|
try {
|
||||||
|
if (flag == Flag.DELETED && set) {
|
||||||
|
delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalMessage.super.setFlag(flag, set);
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
throw new WrappedException(e);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Set the flags on the message.
|
||||||
|
*/
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put("flags", LocalMessage.this.localStore.serializeFlags(getFlags()));
|
||||||
|
cv.put("read", isSet(Flag.SEEN) ? 1 : 0);
|
||||||
|
cv.put("flagged", isSet(Flag.FLAGGED) ? 1 : 0);
|
||||||
|
cv.put("answered", isSet(Flag.ANSWERED) ? 1 : 0);
|
||||||
|
cv.put("forwarded", isSet(Flag.FORWARDED) ? 1 : 0);
|
||||||
|
|
||||||
|
db.update("messages", cv, "id = ?", new String[] { Long.toString(mId) });
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (WrappedException e) {
|
||||||
|
throw(MessagingException) e.getCause();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.localStore.notifyChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If a message is being marked as deleted we want to clear out it's content
|
||||||
|
* and attachments as well. Delete will not actually remove the row since we need
|
||||||
|
* to retain the uid for synchronization purposes.
|
||||||
|
*/
|
||||||
|
private void delete() throws MessagingException
|
||||||
|
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Delete all of the message's content to save space.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
this.localStore.database.execute(true, new DbCallback<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException,
|
||||||
|
UnavailableStorageException {
|
||||||
|
String[] idArg = new String[] { Long.toString(mId) };
|
||||||
|
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put("deleted", 1);
|
||||||
|
cv.put("empty", 1);
|
||||||
|
cv.putNull("subject");
|
||||||
|
cv.putNull("sender_list");
|
||||||
|
cv.putNull("date");
|
||||||
|
cv.putNull("to_list");
|
||||||
|
cv.putNull("cc_list");
|
||||||
|
cv.putNull("bcc_list");
|
||||||
|
cv.putNull("preview");
|
||||||
|
cv.putNull("html_content");
|
||||||
|
cv.putNull("text_content");
|
||||||
|
cv.putNull("reply_to_list");
|
||||||
|
|
||||||
|
db.update("messages", cv, "id = ?", idArg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete all of the message's attachments to save space.
|
||||||
|
* We do this explicit deletion here because we're not deleting the record
|
||||||
|
* in messages, which means our ON DELETE trigger for messages won't cascade
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
((LocalFolder) mFolder).deleteAttachments(mId);
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
throw new WrappedException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.delete("attachments", "message_id = ?", idArg);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (WrappedException e) {
|
||||||
|
throw(MessagingException) e.getCause();
|
||||||
|
}
|
||||||
|
((LocalFolder)mFolder).deleteHeaders(mId);
|
||||||
|
|
||||||
|
this.localStore.notifyChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Completely remove a message from the local database
|
||||||
|
*
|
||||||
|
* TODO: document how this updates the thread structure
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void destroy() throws MessagingException {
|
||||||
|
try {
|
||||||
|
this.localStore.database.execute(true, new DbCallback<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException,
|
||||||
|
UnavailableStorageException {
|
||||||
|
try {
|
||||||
|
LocalFolder localFolder = (LocalFolder) mFolder;
|
||||||
|
|
||||||
|
localFolder.deleteAttachments(mId);
|
||||||
|
|
||||||
|
if (hasThreadChildren(db, mId)) {
|
||||||
|
// This message has children in the thread structure so we need to
|
||||||
|
// make it an empty message.
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put("id", mId);
|
||||||
|
cv.put("folder_id", localFolder.getId());
|
||||||
|
cv.put("deleted", 0);
|
||||||
|
cv.put("message_id", getMessageId());
|
||||||
|
cv.put("empty", 1);
|
||||||
|
|
||||||
|
db.replace("messages", null, cv);
|
||||||
|
|
||||||
|
// Nothing else to do
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the message ID of the parent message if it's empty
|
||||||
|
long currentId = getEmptyThreadParent(db, mId);
|
||||||
|
|
||||||
|
// Delete the placeholder message
|
||||||
|
deleteMessageRow(db, mId);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk the thread tree to delete all empty parents without children
|
||||||
|
*/
|
||||||
|
|
||||||
|
while (currentId != -1) {
|
||||||
|
if (hasThreadChildren(db, currentId)) {
|
||||||
|
// We made sure there are no empty leaf nodes and can stop now.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ID of the (empty) parent for the next iteration
|
||||||
|
long newId = getEmptyThreadParent(db, currentId);
|
||||||
|
|
||||||
|
// Delete the empty message
|
||||||
|
deleteMessageRow(db, currentId);
|
||||||
|
|
||||||
|
currentId = newId;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (MessagingException e) {
|
||||||
|
throw new WrappedException(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (WrappedException e) {
|
||||||
|
throw(MessagingException) e.getCause();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.localStore.notifyChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ID of the the given message's parent if the parent is an empty message.
|
||||||
|
*
|
||||||
|
* @param db
|
||||||
|
* {@link SQLiteDatabase} instance to access the database.
|
||||||
|
* @param messageId
|
||||||
|
* The database ID of the message to get the parent for.
|
||||||
|
*
|
||||||
|
* @return Message ID of the parent message if there exists a parent and it is empty.
|
||||||
|
* Otherwise {@code -1}.
|
||||||
|
*/
|
||||||
|
private long getEmptyThreadParent(SQLiteDatabase db, long messageId) {
|
||||||
|
Cursor cursor = db.rawQuery(
|
||||||
|
"SELECT m.id " +
|
||||||
|
"FROM threads t1 " +
|
||||||
|
"JOIN threads t2 ON (t1.parent = t2.id) " +
|
||||||
|
"LEFT JOIN messages m ON (t2.message_id = m.id) " +
|
||||||
|
"WHERE t1.message_id = ? AND m.empty = 1",
|
||||||
|
new String[] { Long.toString(messageId) });
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (cursor.moveToFirst() && !cursor.isNull(0)) ? cursor.getLong(0) : -1;
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether or not a message has child messages in the thread structure.
|
||||||
|
*
|
||||||
|
* @param db
|
||||||
|
* {@link SQLiteDatabase} instance to access the database.
|
||||||
|
* @param messageId
|
||||||
|
* The database ID of the message to get the children for.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the message has children. {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
private boolean hasThreadChildren(SQLiteDatabase db, long messageId) {
|
||||||
|
Cursor cursor = db.rawQuery(
|
||||||
|
"SELECT COUNT(t2.id) " +
|
||||||
|
"FROM threads t1 " +
|
||||||
|
"JOIN threads t2 ON (t2.parent = t1.id) " +
|
||||||
|
"WHERE t1.message_id = ?",
|
||||||
|
new String[] { Long.toString(messageId) });
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (cursor.moveToFirst() && !cursor.isNull(0) && cursor.getLong(0) > 0L);
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a message from the 'messages' and 'threads' tables.
|
||||||
|
*
|
||||||
|
* @param db
|
||||||
|
* {@link SQLiteDatabase} instance to access the database.
|
||||||
|
* @param messageId
|
||||||
|
* The database ID of the message to delete.
|
||||||
|
*/
|
||||||
|
private void deleteMessageRow(SQLiteDatabase db, long messageId) {
|
||||||
|
String[] idArg = { Long.toString(messageId) };
|
||||||
|
|
||||||
|
// Delete the message
|
||||||
|
db.delete("messages", "id = ?", idArg);
|
||||||
|
|
||||||
|
// Delete row in 'threads' table
|
||||||
|
// TODO: create trigger for 'messages' table to get rid of the row in 'threads' table
|
||||||
|
db.delete("threads", "message_id = ?", idArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadHeaders() throws UnavailableStorageException {
|
||||||
|
ArrayList<LocalMessage> messages = new ArrayList<LocalMessage>();
|
||||||
|
messages.add(this);
|
||||||
|
mHeadersLoaded = true; // set true before calling populate headers to stop recursion
|
||||||
|
((LocalFolder) mFolder).populateHeaders(messages);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addHeader(String name, String value) throws UnavailableStorageException {
|
||||||
|
if (!mHeadersLoaded)
|
||||||
|
loadHeaders();
|
||||||
|
super.addHeader(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHeader(String name, String value) throws UnavailableStorageException {
|
||||||
|
if (!mHeadersLoaded)
|
||||||
|
loadHeaders();
|
||||||
|
super.setHeader(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getHeader(String name) throws UnavailableStorageException {
|
||||||
|
if (!mHeadersLoaded)
|
||||||
|
loadHeaders();
|
||||||
|
return super.getHeader(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeHeader(String name) throws UnavailableStorageException {
|
||||||
|
if (!mHeadersLoaded)
|
||||||
|
loadHeaders();
|
||||||
|
super.removeHeader(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getHeaderNames() throws UnavailableStorageException {
|
||||||
|
if (!mHeadersLoaded)
|
||||||
|
loadHeaders();
|
||||||
|
return super.getHeaderNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocalMessage clone() {
|
||||||
|
LocalMessage message = new LocalMessage(this.localStore);
|
||||||
|
super.copy(message);
|
||||||
|
|
||||||
|
message.mId = mId;
|
||||||
|
message.mAttachmentCount = mAttachmentCount;
|
||||||
|
message.mSubject = mSubject;
|
||||||
|
message.mPreview = mPreview;
|
||||||
|
message.mHeadersLoaded = mHeadersLoaded;
|
||||||
|
message.mMessageDirty = mMessageDirty;
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getThreadId() {
|
||||||
|
return mThreadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRootId() {
|
||||||
|
return mRootId;
|
||||||
|
}
|
||||||
|
}
|
1046
src/com/fsck/k9/mail/store/local/LocalStore.java
Normal file
1046
src/com/fsck/k9/mail/store/local/LocalStore.java
Normal file
File diff suppressed because it is too large
Load Diff
28
src/com/fsck/k9/mail/store/local/LocalTextBody.java
Normal file
28
src/com/fsck/k9/mail/store/local/LocalTextBody.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.internet.TextBody;
|
||||||
|
|
||||||
|
public class LocalTextBody extends TextBody {
|
||||||
|
/**
|
||||||
|
* This is an HTML-ified version of the message for display purposes.
|
||||||
|
*/
|
||||||
|
private String mBodyForDisplay;
|
||||||
|
|
||||||
|
public LocalTextBody(String body) {
|
||||||
|
super(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalTextBody(String body, String bodyForDisplay) {
|
||||||
|
super(body);
|
||||||
|
this.mBodyForDisplay = bodyForDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBodyForDisplay() {
|
||||||
|
return mBodyForDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBodyForDisplay(String mBodyForDisplay) {
|
||||||
|
this.mBodyForDisplay = mBodyForDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
}//LocalTextBody
|
599
src/com/fsck/k9/mail/store/local/StoreSchemaDefinition.java
Normal file
599
src/com/fsck/k9/mail/store/local/StoreSchemaDefinition.java
Normal file
@ -0,0 +1,599 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.fsck.k9.Account;
|
||||||
|
import com.fsck.k9.K9;
|
||||||
|
import com.fsck.k9.R;
|
||||||
|
import com.fsck.k9.helper.Utility;
|
||||||
|
import com.fsck.k9.mail.Flag;
|
||||||
|
import com.fsck.k9.mail.Folder;
|
||||||
|
import com.fsck.k9.mail.Message;
|
||||||
|
import com.fsck.k9.mail.store.LockableDatabase;
|
||||||
|
import com.fsck.k9.provider.AttachmentProvider;
|
||||||
|
|
||||||
|
class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private final LocalStore localStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param localStore
|
||||||
|
*/
|
||||||
|
StoreSchemaDefinition(LocalStore localStore) {
|
||||||
|
this.localStore = localStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return LocalStore.DB_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doDbUpgrade(final SQLiteDatabase db) {
|
||||||
|
try {
|
||||||
|
upgradeDatabase(db);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Exception while upgrading database. Resetting the DB to v0", e);
|
||||||
|
db.setVersion(0);
|
||||||
|
upgradeDatabase(db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upgradeDatabase(final SQLiteDatabase db) {
|
||||||
|
Log.i(K9.LOG_TAG, String.format(Locale.US, "Upgrading database from version %d to version %d",
|
||||||
|
db.getVersion(), LocalStore.DB_VERSION));
|
||||||
|
|
||||||
|
AttachmentProvider.clear(this.localStore.mApplication);
|
||||||
|
|
||||||
|
db.beginTransaction();
|
||||||
|
try {
|
||||||
|
// schema version 29 was when we moved to incremental updates
|
||||||
|
// in the case of a new db or a < v29 db, we blow away and start from scratch
|
||||||
|
if (db.getVersion() < 29) {
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS folders");
|
||||||
|
db.execSQL("CREATE TABLE folders (id INTEGER PRIMARY KEY, name TEXT, "
|
||||||
|
+ "last_updated INTEGER, unread_count INTEGER, visible_limit INTEGER, status TEXT, "
|
||||||
|
+ "push_state TEXT, last_pushed INTEGER, flagged_count INTEGER default 0, "
|
||||||
|
+ "integrate INTEGER, top_group INTEGER, poll_class TEXT, push_class TEXT, display_class TEXT, notify_class TEXT"
|
||||||
|
+ ")");
|
||||||
|
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS folder_name ON folders (name)");
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS messages");
|
||||||
|
db.execSQL("CREATE TABLE messages (" +
|
||||||
|
"id INTEGER PRIMARY KEY, " +
|
||||||
|
"deleted INTEGER default 0, " +
|
||||||
|
"folder_id INTEGER, " +
|
||||||
|
"uid TEXT, " +
|
||||||
|
"subject TEXT, " +
|
||||||
|
"date INTEGER, " +
|
||||||
|
"flags TEXT, " +
|
||||||
|
"sender_list TEXT, " +
|
||||||
|
"to_list TEXT, " +
|
||||||
|
"cc_list TEXT, " +
|
||||||
|
"bcc_list TEXT, " +
|
||||||
|
"reply_to_list TEXT, " +
|
||||||
|
"html_content TEXT, " +
|
||||||
|
"text_content TEXT, " +
|
||||||
|
"attachment_count INTEGER, " +
|
||||||
|
"internal_date INTEGER, " +
|
||||||
|
"message_id TEXT, " +
|
||||||
|
"preview TEXT, " +
|
||||||
|
"mime_type TEXT, "+
|
||||||
|
"normalized_subject_hash INTEGER, " +
|
||||||
|
"empty INTEGER, " +
|
||||||
|
"read INTEGER default 0, " +
|
||||||
|
"flagged INTEGER default 0, " +
|
||||||
|
"answered INTEGER default 0, " +
|
||||||
|
"forwarded INTEGER default 0" +
|
||||||
|
")");
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS headers");
|
||||||
|
db.execSQL("CREATE TABLE headers (id INTEGER PRIMARY KEY, message_id INTEGER, name TEXT, value TEXT)");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS header_folder ON headers (message_id)");
|
||||||
|
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_uid ON messages (uid, folder_id)");
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_folder_id");
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_folder_id_date");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_folder_id_deleted_date ON messages (folder_id,deleted,internal_date)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_empty");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_empty ON messages (empty)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_read");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_read ON messages (read)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_flagged");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_flagged ON messages (flagged)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_composite");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_composite ON messages (deleted, empty,folder_id,flagged,read)");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS threads");
|
||||||
|
db.execSQL("CREATE TABLE threads (" +
|
||||||
|
"id INTEGER PRIMARY KEY, " +
|
||||||
|
"message_id INTEGER, " +
|
||||||
|
"root INTEGER, " +
|
||||||
|
"parent INTEGER" +
|
||||||
|
")");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_message_id");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_message_id ON threads (message_id)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_root");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_root ON threads (root)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_parent");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_parent ON threads (parent)");
|
||||||
|
|
||||||
|
db.execSQL("DROP TRIGGER IF EXISTS set_thread_root");
|
||||||
|
db.execSQL("CREATE TRIGGER set_thread_root " +
|
||||||
|
"AFTER INSERT ON threads " +
|
||||||
|
"BEGIN " +
|
||||||
|
"UPDATE threads SET root=id WHERE root IS NULL AND ROWID = NEW.ROWID; " +
|
||||||
|
"END");
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS attachments");
|
||||||
|
db.execSQL("CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER,"
|
||||||
|
+ "store_data TEXT, content_uri TEXT, size INTEGER, name TEXT,"
|
||||||
|
+ "mime_type TEXT, content_id TEXT, content_disposition TEXT)");
|
||||||
|
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS pending_commands");
|
||||||
|
db.execSQL("CREATE TABLE pending_commands " +
|
||||||
|
"(id INTEGER PRIMARY KEY, command TEXT, arguments TEXT)");
|
||||||
|
|
||||||
|
db.execSQL("DROP TRIGGER IF EXISTS delete_folder");
|
||||||
|
db.execSQL("CREATE TRIGGER delete_folder BEFORE DELETE ON folders BEGIN DELETE FROM messages WHERE old.id = folder_id; END;");
|
||||||
|
|
||||||
|
db.execSQL("DROP TRIGGER IF EXISTS delete_message");
|
||||||
|
db.execSQL("CREATE TRIGGER delete_message BEFORE DELETE ON messages BEGIN DELETE FROM attachments WHERE old.id = message_id; "
|
||||||
|
+ "DELETE FROM headers where old.id = message_id; END;");
|
||||||
|
} else {
|
||||||
|
// in the case that we're starting out at 29 or newer, run all the needed updates
|
||||||
|
|
||||||
|
if (db.getVersion() < 30) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE messages ADD deleted INTEGER default 0");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.toString().startsWith("duplicate column name: deleted")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 31) {
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_folder_id_date");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_folder_id_deleted_date ON messages (folder_id,deleted,internal_date)");
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 32) {
|
||||||
|
db.execSQL("UPDATE messages SET deleted = 1 WHERE flags LIKE '%DELETED%'");
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 33) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE messages ADD preview TEXT");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.toString().startsWith("duplicate column name: preview")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 34) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE folders ADD flagged_count INTEGER default 0");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.getMessage().startsWith("duplicate column name: flagged_count")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 35) {
|
||||||
|
try {
|
||||||
|
db.execSQL("update messages set flags = replace(flags, 'X_NO_SEEN_INFO', 'X_BAD_FLAG')");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to get rid of obsolete flag X_NO_SEEN_INFO", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 36) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE attachments ADD content_id TEXT");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to add content_id column to attachments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 37) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE attachments ADD content_disposition TEXT");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to add content_disposition column to attachments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database version 38 is solely to prune cached attachments now that we clear them better
|
||||||
|
if (db.getVersion() < 39) {
|
||||||
|
try {
|
||||||
|
db.execSQL("DELETE FROM headers WHERE id in (SELECT headers.id FROM headers LEFT JOIN messages ON headers.message_id = messages.id WHERE messages.id IS NULL)");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to remove extra header data from the database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// V40: Store the MIME type for a message.
|
||||||
|
if (db.getVersion() < 40) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE messages ADD mime_type TEXT");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Unable to add mime_type column to messages");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db.getVersion() < 41) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE folders ADD integrate INTEGER");
|
||||||
|
db.execSQL("ALTER TABLE folders ADD top_group INTEGER");
|
||||||
|
db.execSQL("ALTER TABLE folders ADD poll_class TEXT");
|
||||||
|
db.execSQL("ALTER TABLE folders ADD push_class TEXT");
|
||||||
|
db.execSQL("ALTER TABLE folders ADD display_class TEXT");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
SharedPreferences prefs = this.localStore.getPreferences();
|
||||||
|
cursor = db.rawQuery("SELECT id, name FROM folders", null);
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
try {
|
||||||
|
int id = cursor.getInt(0);
|
||||||
|
String name = cursor.getString(1);
|
||||||
|
update41Metadata(db, prefs, id, name);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(K9.LOG_TAG, " error trying to ugpgrade a folder class", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Exception while upgrading database to v41. folder classes may have vanished", e);
|
||||||
|
} finally {
|
||||||
|
Utility.closeQuietly(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() == 41) {
|
||||||
|
try {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
SharedPreferences.Editor editor = this.localStore.getPreferences().edit();
|
||||||
|
|
||||||
|
List <? extends Folder > folders = this.localStore.getPersonalNamespaces(true);
|
||||||
|
for (Folder folder : folders) {
|
||||||
|
if (folder instanceof LocalFolder) {
|
||||||
|
LocalFolder lFolder = (LocalFolder)folder;
|
||||||
|
lFolder.save(editor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.commit();
|
||||||
|
long endTime = System.currentTimeMillis();
|
||||||
|
Log.i(K9.LOG_TAG, "Putting folder preferences for " + folders.size() + " folders back into Preferences took " + (endTime - startTime) + " ms");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Could not replace Preferences in upgrade from DB_VERSION 41", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 43) {
|
||||||
|
try {
|
||||||
|
// If folder "OUTBOX" (old, v3.800 - v3.802) exists, rename it to
|
||||||
|
// "K9MAIL_INTERNAL_OUTBOX" (new)
|
||||||
|
LocalFolder oldOutbox = new LocalFolder(this.localStore, "OUTBOX");
|
||||||
|
if (oldOutbox.exists()) {
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put("name", Account.OUTBOX);
|
||||||
|
db.update("folders", cv, "name = ?", new String[] { "OUTBOX" });
|
||||||
|
Log.i(K9.LOG_TAG, "Renamed folder OUTBOX to " + Account.OUTBOX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if old (pre v3.800) localized outbox folder exists
|
||||||
|
String localizedOutbox = K9.app.getString(R.string.special_mailbox_name_outbox);
|
||||||
|
LocalFolder obsoleteOutbox = new LocalFolder(this.localStore, localizedOutbox);
|
||||||
|
if (obsoleteOutbox.exists()) {
|
||||||
|
// Get all messages from the localized outbox ...
|
||||||
|
Message[] messages = obsoleteOutbox.getMessages(null, false);
|
||||||
|
|
||||||
|
if (messages.length > 0) {
|
||||||
|
// ... and move them to the drafts folder (we don't want to
|
||||||
|
// surprise the user by sending potentially very old messages)
|
||||||
|
LocalFolder drafts = new LocalFolder(this.localStore, this.localStore.getAccount().getDraftsFolderName());
|
||||||
|
obsoleteOutbox.moveMessages(messages, drafts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now get rid of the localized outbox
|
||||||
|
obsoleteOutbox.delete();
|
||||||
|
obsoleteOutbox.delete(true);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(K9.LOG_TAG, "Error trying to fix the outbox folders", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 44) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE messages ADD thread_root INTEGER");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD thread_parent INTEGER");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD normalized_subject_hash INTEGER");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD empty INTEGER");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 45) {
|
||||||
|
try {
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_empty");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_empty ON messages (empty)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_thread_root");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_thread_root ON messages (thread_root)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_thread_parent");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_thread_parent ON messages (thread_parent)");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 46) {
|
||||||
|
db.execSQL("ALTER TABLE messages ADD read INTEGER default 0");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD flagged INTEGER default 0");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD answered INTEGER default 0");
|
||||||
|
db.execSQL("ALTER TABLE messages ADD forwarded INTEGER default 0");
|
||||||
|
|
||||||
|
String[] projection = { "id", "flags" };
|
||||||
|
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
List<Flag> extraFlags = new ArrayList<Flag>();
|
||||||
|
|
||||||
|
Cursor cursor = db.query("messages", projection, null, null, null, null, null);
|
||||||
|
try {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
long id = cursor.getLong(0);
|
||||||
|
String flagList = cursor.getString(1);
|
||||||
|
|
||||||
|
boolean read = false;
|
||||||
|
boolean flagged = false;
|
||||||
|
boolean answered = false;
|
||||||
|
boolean forwarded = false;
|
||||||
|
|
||||||
|
if (flagList != null && flagList.length() > 0) {
|
||||||
|
String[] flags = flagList.split(",");
|
||||||
|
|
||||||
|
for (String flagStr : flags) {
|
||||||
|
try {
|
||||||
|
Flag flag = Flag.valueOf(flagStr);
|
||||||
|
|
||||||
|
switch (flag) {
|
||||||
|
case ANSWERED: {
|
||||||
|
answered = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DELETED: {
|
||||||
|
// Don't store this in column 'flags'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FLAGGED: {
|
||||||
|
flagged = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FORWARDED: {
|
||||||
|
forwarded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SEEN: {
|
||||||
|
read = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DRAFT:
|
||||||
|
case RECENT:
|
||||||
|
case X_DESTROYED:
|
||||||
|
case X_DOWNLOADED_FULL:
|
||||||
|
case X_DOWNLOADED_PARTIAL:
|
||||||
|
case X_GOT_ALL_HEADERS:
|
||||||
|
case X_REMOTE_COPY_STARTED:
|
||||||
|
case X_SEND_FAILED:
|
||||||
|
case X_SEND_IN_PROGRESS: {
|
||||||
|
extraFlags.add(flag);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Ignore bad flags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cv.put("flags", this.localStore.serializeFlags(extraFlags.toArray(LocalStore.EMPTY_FLAG_ARRAY)));
|
||||||
|
cv.put("read", read);
|
||||||
|
cv.put("flagged", flagged);
|
||||||
|
cv.put("answered", answered);
|
||||||
|
cv.put("forwarded", forwarded);
|
||||||
|
|
||||||
|
db.update("messages", cv, "id = ?", new String[] { Long.toString(id) });
|
||||||
|
|
||||||
|
cv.clear();
|
||||||
|
extraFlags.clear();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_read ON messages (read)");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_flagged ON messages (flagged)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db.getVersion() < 47) {
|
||||||
|
// Create new 'threads' table
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS threads");
|
||||||
|
db.execSQL("CREATE TABLE threads (" +
|
||||||
|
"id INTEGER PRIMARY KEY, " +
|
||||||
|
"message_id INTEGER, " +
|
||||||
|
"root INTEGER, " +
|
||||||
|
"parent INTEGER" +
|
||||||
|
")");
|
||||||
|
|
||||||
|
// Create indices for new table
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_message_id");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_message_id ON threads (message_id)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_root");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_root ON threads (root)");
|
||||||
|
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS threads_parent");
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS threads_parent ON threads (parent)");
|
||||||
|
|
||||||
|
// Create entries for all messages in 'threads' table
|
||||||
|
db.execSQL("INSERT INTO threads (message_id) SELECT id FROM messages");
|
||||||
|
|
||||||
|
// Copy thread structure from 'messages' table to 'threads'
|
||||||
|
Cursor cursor = db.query("messages",
|
||||||
|
new String[] { "id", "thread_root", "thread_parent" },
|
||||||
|
null, null, null, null, null);
|
||||||
|
try {
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
cv.clear();
|
||||||
|
long messageId = cursor.getLong(0);
|
||||||
|
|
||||||
|
if (!cursor.isNull(1)) {
|
||||||
|
long threadRootMessageId = cursor.getLong(1);
|
||||||
|
db.execSQL("UPDATE threads SET root = (SELECT t.id FROM " +
|
||||||
|
"threads t WHERE t.message_id = ?) " +
|
||||||
|
"WHERE message_id = ?",
|
||||||
|
new String[] {
|
||||||
|
Long.toString(threadRootMessageId),
|
||||||
|
Long.toString(messageId)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cursor.isNull(2)) {
|
||||||
|
long threadParentMessageId = cursor.getLong(2);
|
||||||
|
db.execSQL("UPDATE threads SET parent = (SELECT t.id FROM " +
|
||||||
|
"threads t WHERE t.message_id = ?) " +
|
||||||
|
"WHERE message_id = ?",
|
||||||
|
new String[] {
|
||||||
|
Long.toString(threadParentMessageId),
|
||||||
|
Long.toString(messageId)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove indices for old thread-related columns in 'messages' table
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_thread_root");
|
||||||
|
db.execSQL("DROP INDEX IF EXISTS msg_thread_parent");
|
||||||
|
|
||||||
|
// Clear out old thread-related columns in 'messages'
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.putNull("thread_root");
|
||||||
|
cv.putNull("thread_parent");
|
||||||
|
db.update("messages", cv, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db.getVersion() < 48) {
|
||||||
|
db.execSQL("UPDATE threads SET root=id WHERE root IS NULL");
|
||||||
|
|
||||||
|
db.execSQL("CREATE TRIGGER set_thread_root " +
|
||||||
|
"AFTER INSERT ON threads " +
|
||||||
|
"BEGIN " +
|
||||||
|
"UPDATE threads SET root=id WHERE root IS NULL AND ROWID = NEW.ROWID; " +
|
||||||
|
"END");
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 49) {
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS msg_composite ON messages (deleted, empty,folder_id,flagged,read)");
|
||||||
|
|
||||||
|
}
|
||||||
|
if (db.getVersion() < 50) {
|
||||||
|
try {
|
||||||
|
db.execSQL("ALTER TABLE folders ADD notify_class TEXT default '" +
|
||||||
|
Folder.FolderClass.INHERITED.name() + "'");
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
if (! e.getMessage().startsWith("duplicate column name:")) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put("notify_class", Folder.FolderClass.FIRST_CLASS.name());
|
||||||
|
|
||||||
|
db.update("folders", cv, "name = ?",
|
||||||
|
new String[] { this.localStore.getAccount().getInboxFolderName() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.setVersion(LocalStore.DB_VERSION);
|
||||||
|
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db.getVersion() != LocalStore.DB_VERSION) {
|
||||||
|
throw new RuntimeException("Database upgrade failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update41Metadata(final SQLiteDatabase db, SharedPreferences prefs, int id, String name) {
|
||||||
|
|
||||||
|
|
||||||
|
Folder.FolderClass displayClass = Folder.FolderClass.NO_CLASS;
|
||||||
|
Folder.FolderClass syncClass = Folder.FolderClass.INHERITED;
|
||||||
|
Folder.FolderClass pushClass = Folder.FolderClass.SECOND_CLASS;
|
||||||
|
boolean inTopGroup = false;
|
||||||
|
boolean integrate = false;
|
||||||
|
if (this.localStore.getAccount().getInboxFolderName().equals(name)) {
|
||||||
|
displayClass = Folder.FolderClass.FIRST_CLASS;
|
||||||
|
syncClass = Folder.FolderClass.FIRST_CLASS;
|
||||||
|
pushClass = Folder.FolderClass.FIRST_CLASS;
|
||||||
|
inTopGroup = true;
|
||||||
|
integrate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
displayClass = Folder.FolderClass.valueOf(prefs.getString(this.localStore.uUid + "." + name + ".displayMode", displayClass.name()));
|
||||||
|
syncClass = Folder.FolderClass.valueOf(prefs.getString(this.localStore.uUid + "." + name + ".syncMode", syncClass.name()));
|
||||||
|
pushClass = Folder.FolderClass.valueOf(prefs.getString(this.localStore.uUid + "." + name + ".pushMode", pushClass.name()));
|
||||||
|
inTopGroup = prefs.getBoolean(this.localStore.uUid + "." + name + ".inTopGroup", inTopGroup);
|
||||||
|
integrate = prefs.getBoolean(this.localStore.uUid + "." + name + ".integrate", integrate);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(K9.LOG_TAG, " Throwing away an error while trying to upgrade folder metadata", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayClass == Folder.FolderClass.NONE) {
|
||||||
|
displayClass = Folder.FolderClass.NO_CLASS;
|
||||||
|
}
|
||||||
|
if (syncClass == Folder.FolderClass.NONE) {
|
||||||
|
syncClass = Folder.FolderClass.INHERITED;
|
||||||
|
}
|
||||||
|
if (pushClass == Folder.FolderClass.NONE) {
|
||||||
|
pushClass = Folder.FolderClass.INHERITED;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.execSQL("UPDATE folders SET integrate = ?, top_group = ?, poll_class=?, push_class =?, display_class = ? WHERE id = ?",
|
||||||
|
new Object[] { integrate, inTopGroup, syncClass, pushClass, displayClass, id });
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
26
src/com/fsck/k9/mail/store/local/TempFileBody.java
Normal file
26
src/com/fsck/k9/mail/store/local/TempFileBody.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
|
||||||
|
public class TempFileBody extends BinaryAttachmentBody {
|
||||||
|
private final File mFile;
|
||||||
|
|
||||||
|
public TempFileBody(String filename) {
|
||||||
|
mFile = new File(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws MessagingException {
|
||||||
|
try {
|
||||||
|
return new FileInputStream(mFile);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return new ByteArrayInputStream(LocalStore.EMPTY_BYTE_ARRAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
src/com/fsck/k9/mail/store/local/TempFileMessageBody.java
Normal file
36
src/com/fsck/k9/mail/store/local/TempFileMessageBody.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.apache.james.mime4j.util.MimeUtil;
|
||||||
|
|
||||||
|
import com.fsck.k9.mail.CompositeBody;
|
||||||
|
import com.fsck.k9.mail.MessagingException;
|
||||||
|
|
||||||
|
public class TempFileMessageBody extends TempFileBody implements CompositeBody {
|
||||||
|
|
||||||
|
public TempFileMessageBody(String filename) {
|
||||||
|
super(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream out) throws IOException, MessagingException {
|
||||||
|
AttachmentMessageBodyUtil.writeTo(this, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsing7bitTransport() throws MessagingException {
|
||||||
|
// see LocalAttachmentMessageBody.setUsing7bitTransport()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEncoding(String encoding) throws MessagingException {
|
||||||
|
if (!MimeUtil.ENC_7BIT.equalsIgnoreCase(encoding)
|
||||||
|
&& !MimeUtil.ENC_8BIT.equalsIgnoreCase(encoding)) {
|
||||||
|
throw new MessagingException(
|
||||||
|
"Incompatible content-transfer-encoding applied to a CompositeBody");
|
||||||
|
}
|
||||||
|
mEncoding = encoding;
|
||||||
|
}
|
||||||
|
}
|
17
src/com/fsck/k9/mail/store/local/ThreadInfo.java
Normal file
17
src/com/fsck/k9/mail/store/local/ThreadInfo.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.fsck.k9.mail.store.local;
|
||||||
|
|
||||||
|
class ThreadInfo {
|
||||||
|
public final long threadId;
|
||||||
|
public final long msgId;
|
||||||
|
public final String messageId;
|
||||||
|
public final long rootId;
|
||||||
|
public final long parentId;
|
||||||
|
|
||||||
|
public ThreadInfo(long threadId, long msgId, String messageId, long rootId, long parentId) {
|
||||||
|
this.threadId = threadId;
|
||||||
|
this.msgId = msgId;
|
||||||
|
this.messageId = messageId;
|
||||||
|
this.rootId = rootId;
|
||||||
|
this.parentId = parentId;
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ import com.fsck.k9.mail.filter.LineWrapOutputStream;
|
|||||||
import com.fsck.k9.mail.filter.PeekableInputStream;
|
import com.fsck.k9.mail.filter.PeekableInputStream;
|
||||||
import com.fsck.k9.mail.filter.SmtpDataStuffing;
|
import com.fsck.k9.mail.filter.SmtpDataStuffing;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||||
import com.fsck.k9.net.ssl.SslHelper;
|
import com.fsck.k9.net.ssl.SslHelper;
|
||||||
|
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
|
@ -15,8 +15,8 @@ import com.fsck.k9.K9;
|
|||||||
import com.fsck.k9.Preferences;
|
import com.fsck.k9.Preferences;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
import com.fsck.k9.mail.store.local.LocalStore;
|
||||||
import com.fsck.k9.mail.store.LocalStore.AttachmentInfo;
|
import com.fsck.k9.mail.store.local.LocalStore.AttachmentInfo;
|
||||||
import com.fsck.k9.mail.store.StorageManager;
|
import com.fsck.k9.mail.store.StorageManager;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
@ -11,10 +11,10 @@ import com.fsck.k9.cache.EmailProviderCacheCursor;
|
|||||||
import com.fsck.k9.helper.StringUtils;
|
import com.fsck.k9.helper.StringUtils;
|
||||||
import com.fsck.k9.helper.Utility;
|
import com.fsck.k9.helper.Utility;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
|
||||||
import com.fsck.k9.mail.store.LockableDatabase;
|
import com.fsck.k9.mail.store.LockableDatabase;
|
||||||
import com.fsck.k9.mail.store.LockableDatabase.DbCallback;
|
import com.fsck.k9.mail.store.LockableDatabase.DbCallback;
|
||||||
import com.fsck.k9.mail.store.LockableDatabase.WrappedException;
|
import com.fsck.k9.mail.store.LockableDatabase.WrappedException;
|
||||||
|
import com.fsck.k9.mail.store.local.LocalStore;
|
||||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||||
import com.fsck.k9.search.SqlQueryBuilder;
|
import com.fsck.k9.search.SqlQueryBuilder;
|
||||||
|
|
||||||
|
@ -32,7 +32,8 @@ import com.fsck.k9.mail.Flag;
|
|||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.Message;
|
import com.fsck.k9.mail.Message;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||||
|
import com.fsck.k9.mail.store.local.LocalStore;
|
||||||
import com.fsck.k9.search.SearchAccount;
|
import com.fsck.k9.search.SearchAccount;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
@ -152,9 +153,9 @@ public class MessageProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the {@link LocalStore.LocalMessage#getId() ID} from the given
|
* Extracts the {@link LocalMessage#getId() ID} from the given
|
||||||
* {@link MessageInfoHolder}. The underlying {@link Message} is expected to
|
* {@link MessageInfoHolder}. The underlying {@link Message} is expected to
|
||||||
* be a {@link LocalStore.LocalMessage}.
|
* be a {@link LocalMessage}.
|
||||||
*/
|
*/
|
||||||
public static class IdExtractor implements FieldExtractor<MessageInfoHolder, Long> {
|
public static class IdExtractor implements FieldExtractor<MessageInfoHolder, Long> {
|
||||||
@Override
|
@Override
|
||||||
|
@ -5,8 +5,8 @@ import java.util.List;
|
|||||||
import com.fsck.k9.Account;
|
import com.fsck.k9.Account;
|
||||||
import com.fsck.k9.mail.MessagingException;
|
import com.fsck.k9.mail.MessagingException;
|
||||||
import com.fsck.k9.mail.Folder;
|
import com.fsck.k9.mail.Folder;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
import com.fsck.k9.mail.store.local.LocalFolder;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
import com.fsck.k9.mail.store.local.LocalStore;
|
||||||
import com.fsck.k9.search.SearchSpecification.Attribute;
|
import com.fsck.k9.search.SearchSpecification.Attribute;
|
||||||
import com.fsck.k9.search.SearchSpecification.SearchCondition;
|
import com.fsck.k9.search.SearchSpecification.SearchCondition;
|
||||||
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
||||||
|
@ -40,7 +40,7 @@ import com.fsck.k9.mail.MessagingException;
|
|||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
import com.fsck.k9.mail.internet.MimeHeader;
|
import com.fsck.k9.mail.internet.MimeHeader;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBodyPart;
|
import com.fsck.k9.mail.store.local.LocalAttachmentBodyPart;
|
||||||
import com.fsck.k9.provider.AttachmentProvider;
|
import com.fsck.k9.provider.AttachmentProvider;
|
||||||
|
|
||||||
public class AttachmentView extends FrameLayout implements OnClickListener, OnLongClickListener {
|
public class AttachmentView extends FrameLayout implements OnClickListener, OnLongClickListener {
|
||||||
|
@ -55,8 +55,8 @@ import com.fsck.k9.mail.MessagingException;
|
|||||||
import com.fsck.k9.mail.Multipart;
|
import com.fsck.k9.mail.Multipart;
|
||||||
import com.fsck.k9.mail.Part;
|
import com.fsck.k9.mail.Part;
|
||||||
import com.fsck.k9.mail.internet.MimeUtility;
|
import com.fsck.k9.mail.internet.MimeUtility;
|
||||||
import com.fsck.k9.mail.store.LocalStore;
|
import com.fsck.k9.mail.store.local.LocalAttachmentBodyPart;
|
||||||
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
|
import com.fsck.k9.mail.store.local.LocalMessage;
|
||||||
import com.fsck.k9.provider.AttachmentProvider.AttachmentProviderColumns;
|
import com.fsck.k9.provider.AttachmentProvider.AttachmentProviderColumns;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
@ -626,7 +626,7 @@ public class SingleMessageView extends LinearLayout implements OnClickListener,
|
|||||||
for (int i = 0; i < mp.getCount(); i++) {
|
for (int i = 0; i < mp.getCount(); i++) {
|
||||||
renderAttachments(mp.getBodyPart(i), depth + 1, message, account, controller, listener);
|
renderAttachments(mp.getBodyPart(i), depth + 1, message, account, controller, listener);
|
||||||
}
|
}
|
||||||
} else if (part instanceof LocalStore.LocalAttachmentBodyPart) {
|
} else if (part instanceof LocalAttachmentBodyPart) {
|
||||||
AttachmentView view = (AttachmentView)mInflater.inflate(R.layout.message_view_attachment, null);
|
AttachmentView view = (AttachmentView)mInflater.inflate(R.layout.message_view_attachment, null);
|
||||||
view.setCallback(attachmentCallback);
|
view.setCallback(attachmentCallback);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user