2008-11-01 17:32:06 -04:00
|
|
|
|
2014-12-14 10:28:42 -05:00
|
|
|
package com.fsck.k9.mailstore;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
|
|
|
import android.app.Application;
|
2012-10-23 18:08:44 -04:00
|
|
|
import android.content.ContentResolver;
|
2008-11-01 17:32:06 -04:00
|
|
|
import android.content.ContentValues;
|
2010-11-13 16:40:56 -05:00
|
|
|
import android.content.Context;
|
2009-12-09 22:16:42 -05:00
|
|
|
import android.content.SharedPreferences;
|
2008-11-01 17:32:06 -04:00
|
|
|
import android.database.Cursor;
|
|
|
|
import android.database.sqlite.SQLiteDatabase;
|
|
|
|
import android.net.Uri;
|
2014-12-11 16:48:22 -05:00
|
|
|
import android.text.TextUtils;
|
2009-12-09 22:16:42 -05:00
|
|
|
import android.util.Log;
|
2010-03-03 23:00:30 -05:00
|
|
|
import com.fsck.k9.Account;
|
2009-12-14 21:50:53 -05:00
|
|
|
import com.fsck.k9.K9;
|
|
|
|
import com.fsck.k9.Preferences;
|
2014-09-29 13:06:21 -04:00
|
|
|
import com.fsck.k9.helper.UrlEncodingHelper;
|
2010-05-19 14:17:06 -04:00
|
|
|
import com.fsck.k9.helper.Utility;
|
2010-11-13 16:40:56 -05:00
|
|
|
import com.fsck.k9.mail.Flag;
|
|
|
|
import com.fsck.k9.mail.Folder;
|
2014-12-12 10:02:59 -05:00
|
|
|
import com.fsck.k9.mail.MessageRetrievalListener;
|
2010-11-13 16:40:56 -05:00
|
|
|
import com.fsck.k9.mail.MessagingException;
|
|
|
|
import com.fsck.k9.mail.Store;
|
2014-12-14 10:28:42 -05:00
|
|
|
import com.fsck.k9.mailstore.StorageManager.StorageProvider;
|
|
|
|
import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
|
|
|
|
import com.fsck.k9.mailstore.LockableDatabase.WrappedException;
|
2012-10-23 18:08:44 -04:00
|
|
|
import com.fsck.k9.provider.EmailProvider;
|
2013-02-18 22:45:14 -05:00
|
|
|
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
2012-10-13 08:53:00 -04:00
|
|
|
import com.fsck.k9.search.LocalSearch;
|
2012-11-02 20:52:45 -04:00
|
|
|
import com.fsck.k9.search.SearchSpecification.Attribute;
|
2015-03-06 15:17:49 -05:00
|
|
|
import com.fsck.k9.search.SearchSpecification.SearchField;
|
2012-10-28 21:27:34 -04:00
|
|
|
import com.fsck.k9.search.SqlQueryBuilder;
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2014-12-12 07:34:57 -05:00
|
|
|
import java.io.File;
|
|
|
|
import java.io.Serializable;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.HashMap;
|
|
|
|
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;
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
/**
|
|
|
|
* <pre>
|
|
|
|
* Implements a SQLite database backed local store for Messages.
|
|
|
|
* </pre>
|
|
|
|
*/
|
2011-02-06 17:09:48 -05:00
|
|
|
public class LocalStore extends Store implements Serializable {
|
2010-08-02 07:55:31 -04:00
|
|
|
|
2011-01-31 18:45:14 -05:00
|
|
|
private static final long serialVersionUID = -5142141896809423072L;
|
|
|
|
|
2014-09-07 10:01:33 -04:00
|
|
|
static final String[] EMPTY_STRING_ARRAY = new String[0];
|
2014-09-07 08:54:02 -04:00
|
|
|
static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
2010-08-02 07:55:31 -04:00
|
|
|
|
2014-12-11 22:23:32 -05:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2014-12-12 01:54:34 -05:00
|
|
|
private static ConcurrentMap<String, LocalStore> sLocalStores = new ConcurrentHashMap<String, LocalStore>();
|
2014-12-11 22:23:32 -05:00
|
|
|
|
2009-12-27 11:53:31 -05:00
|
|
|
/*
|
|
|
|
* a String containing the columns getMessages expects to work with
|
|
|
|
* in the correct order.
|
|
|
|
*/
|
2014-09-07 10:01:33 -04:00
|
|
|
static String GET_MESSAGES_COLS =
|
2013-01-10 21:40:35 -05:00
|
|
|
"subject, sender_list, date, uid, flags, messages.id, to_list, cc_list, " +
|
|
|
|
"bcc_list, reply_to_list, attachment_count, internal_date, messages.message_id, " +
|
|
|
|
"folder_id, preview, threads.id, threads.root, deleted, read, flagged, answered, " +
|
|
|
|
"forwarded ";
|
2009-12-27 11:53:31 -05:00
|
|
|
|
2014-09-07 10:01:33 -04:00
|
|
|
static final String GET_FOLDER_COLS =
|
2013-07-06 20:40:04 -04:00
|
|
|
"folders.id, name, visible_limit, last_updated, status, push_state, last_pushed, " +
|
2014-08-06 05:04:07 -04:00
|
|
|
"integrate, top_group, poll_class, push_class, display_class, notify_class";
|
2013-07-06 20:22:40 -04:00
|
|
|
|
2014-09-07 10:01:33 -04:00
|
|
|
static final int FOLDER_ID_INDEX = 0;
|
|
|
|
static final int FOLDER_NAME_INDEX = 1;
|
|
|
|
static final int FOLDER_VISIBLE_LIMIT_INDEX = 2;
|
|
|
|
static final int FOLDER_LAST_CHECKED_INDEX = 3;
|
|
|
|
static final int FOLDER_STATUS_INDEX = 4;
|
|
|
|
static final int FOLDER_PUSH_STATE_INDEX = 5;
|
|
|
|
static final int FOLDER_LAST_PUSHED_INDEX = 6;
|
|
|
|
static final int FOLDER_INTEGRATE_INDEX = 7;
|
|
|
|
static final int FOLDER_TOP_GROUP_INDEX = 8;
|
|
|
|
static final int FOLDER_SYNC_CLASS_INDEX = 9;
|
|
|
|
static final int FOLDER_PUSH_CLASS_INDEX = 10;
|
|
|
|
static final int FOLDER_DISPLAY_CLASS_INDEX = 11;
|
|
|
|
static final int FOLDER_NOTIFY_CLASS_INDEX = 12;
|
|
|
|
|
|
|
|
static final String[] UID_CHECK_PROJECTION = { "uid" };
|
2012-11-15 15:05:45 -05:00
|
|
|
|
|
|
|
/**
|
2013-03-07 19:15:26 -05:00
|
|
|
* Maximum number of UIDs to check for existence at once.
|
2012-11-15 15:05:45 -05:00
|
|
|
*
|
|
|
|
* @see LocalFolder#extractNewMessages(List)
|
|
|
|
*/
|
2014-09-07 10:01:33 -04:00
|
|
|
static final int UID_CHECK_BATCH_SIZE = 500;
|
2011-01-23 22:27:19 -05:00
|
|
|
|
2012-12-05 23:25:41 -05:00
|
|
|
/**
|
2013-03-07 19:15:26 -05:00
|
|
|
* Maximum number of messages to perform flag updates on at once.
|
2012-12-05 23:25:41 -05:00
|
|
|
*
|
2014-09-28 07:48:46 -04:00
|
|
|
* @see #setFlag(List, Flag, boolean)
|
2012-12-05 23:25:41 -05:00
|
|
|
*/
|
|
|
|
private static final int FLAG_UPDATE_BATCH_SIZE = 500;
|
|
|
|
|
2013-01-11 20:28:12 -05:00
|
|
|
/**
|
2013-03-07 19:15:26 -05:00
|
|
|
* Maximum number of threads to perform flag updates on at once.
|
2013-01-11 20:28:12 -05:00
|
|
|
*
|
|
|
|
* @see #setFlagForThreads(List, Flag, boolean)
|
|
|
|
*/
|
2013-03-07 19:15:26 -05:00
|
|
|
private static final int THREAD_FLAG_UPDATE_BATCH_SIZE = 500;
|
2013-01-11 20:28:12 -05:00
|
|
|
|
2014-08-06 05:04:07 -04:00
|
|
|
public static final int DB_VERSION = 50;
|
2010-11-13 16:40:56 -05:00
|
|
|
|
2013-02-18 22:45:14 -05:00
|
|
|
|
|
|
|
public static String getColumnNameForFlag(Flag flag) {
|
|
|
|
switch (flag) {
|
|
|
|
case SEEN: {
|
|
|
|
return MessageColumns.READ;
|
|
|
|
}
|
|
|
|
case FLAGGED: {
|
|
|
|
return MessageColumns.FLAGGED;
|
|
|
|
}
|
|
|
|
case ANSWERED: {
|
|
|
|
return MessageColumns.ANSWERED;
|
|
|
|
}
|
|
|
|
case FORWARDED: {
|
|
|
|
return MessageColumns.FORWARDED;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
throw new IllegalArgumentException("Flag must be a special column flag");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-18 17:56:40 -05:00
|
|
|
protected String uUid = null;
|
|
|
|
|
2014-12-11 22:40:49 -05:00
|
|
|
final Context context;
|
2010-12-18 17:56:40 -05:00
|
|
|
|
2014-09-07 10:01:33 -04:00
|
|
|
LockableDatabase database;
|
2010-11-13 16:40:56 -05:00
|
|
|
|
2012-10-23 18:08:44 -04:00
|
|
|
private ContentResolver mContentResolver;
|
2014-12-11 22:23:32 -05:00
|
|
|
private final Account mAccount;
|
2012-10-23 18:08:44 -04:00
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
/**
|
2010-03-03 23:00:30 -05:00
|
|
|
* local://localhost/path/to/database/uuid.db
|
2014-12-11 22:40:49 -05:00
|
|
|
* This constructor is only used by {@link Store#getLocalInstance(Account, Context)}
|
2010-12-28 04:10:50 -05:00
|
|
|
* @param account
|
2014-12-11 22:40:49 -05:00
|
|
|
* @param context
|
2010-11-13 16:40:56 -05:00
|
|
|
* @throws UnavailableStorageException if not {@link StorageProvider#isReady(Context)}
|
2008-11-01 17:32:06 -04:00
|
|
|
*/
|
2014-12-11 22:40:49 -05:00
|
|
|
public LocalStore(final Account account, final Context context) throws MessagingException {
|
2014-12-11 22:23:32 -05:00
|
|
|
mAccount = account;
|
2014-12-11 22:40:49 -05:00
|
|
|
database = new LockableDatabase(context, account.getUuid(), new StoreSchemaDefinition(this));
|
2010-12-18 17:56:40 -05:00
|
|
|
|
2014-12-11 22:40:49 -05:00
|
|
|
this.context = context;
|
|
|
|
mContentResolver = context.getContentResolver();
|
2010-12-18 17:56:40 -05:00
|
|
|
database.setStorageProviderId(account.getLocalStorageProviderId());
|
2010-11-13 16:40:56 -05:00
|
|
|
uUid = account.getUuid();
|
|
|
|
|
2010-12-18 17:56:40 -05:00
|
|
|
database.open();
|
2010-11-13 16:40:56 -05:00
|
|
|
}
|
2009-11-17 11:54:50 -05:00
|
|
|
|
2014-12-11 22:23:32 -05:00
|
|
|
/**
|
|
|
|
* Get an instance of a local mail store.
|
|
|
|
*
|
|
|
|
* @throws UnavailableStorageException
|
|
|
|
* if not {@link StorageProvider#isReady(Context)}
|
|
|
|
*/
|
2014-12-12 00:32:24 -05:00
|
|
|
public static LocalStore getInstance(Account account, Context context)
|
2014-12-11 22:23:32 -05:00
|
|
|
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) {
|
2014-12-12 01:54:34 -05:00
|
|
|
LocalStore store = sLocalStores.get(accountUuid);
|
2014-12-11 22:23:32 -05:00
|
|
|
|
|
|
|
if (store == null) {
|
|
|
|
// Creating a LocalStore instance will create or upgrade the database if
|
|
|
|
// necessary. This could take some time.
|
2014-12-12 00:32:24 -05:00
|
|
|
store = new LocalStore(account, context);
|
2014-12-11 22:23:32 -05:00
|
|
|
|
|
|
|
sLocalStores.put(accountUuid, store);
|
|
|
|
}
|
|
|
|
|
2014-12-12 01:54:34 -05:00
|
|
|
return store;
|
2014-12-11 22:23:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-12 07:34:57 -05:00
|
|
|
public static void removeAccount(Account account) {
|
2014-12-11 22:23:32 -05:00
|
|
|
try {
|
2014-12-12 07:34:57 -05:00
|
|
|
removeInstance(account);
|
2014-12-11 22:23:32 -05:00
|
|
|
} catch (Exception e) {
|
2014-12-12 07:34:57 -05:00
|
|
|
Log.e(K9.LOG_TAG, "Failed to reset local store for account " + account.getUuid(), e);
|
2014-12-11 22:23:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-15 21:32:57 -05:00
|
|
|
private static void removeInstance(Account account) {
|
2014-12-11 22:23:32 -05:00
|
|
|
String accountUuid = account.getUuid();
|
|
|
|
sLocalStores.remove(accountUuid);
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void switchLocalStorage(final String newStorageProviderId) throws MessagingException {
|
2010-12-18 17:56:40 -05:00
|
|
|
database.switchProvider(newStorageProviderId);
|
2010-11-13 16:40:56 -05:00
|
|
|
}
|
2010-01-24 15:41:04 -05:00
|
|
|
|
2014-12-11 22:23:32 -05:00
|
|
|
protected Account getAccount() {
|
|
|
|
return mAccount;
|
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
protected SharedPreferences getPreferences() {
|
2014-12-11 22:40:49 -05:00
|
|
|
return Preferences.getPreferences(context).getPreferences();
|
2011-02-04 07:26:14 -05:00
|
|
|
}
|
|
|
|
|
2014-12-12 10:02:59 -05:00
|
|
|
public long getSize() throws MessagingException {
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2014-12-11 22:40:49 -05:00
|
|
|
final StorageManager storageManager = StorageManager.getInstance(context);
|
2010-11-13 16:40:56 -05:00
|
|
|
|
|
|
|
final File attachmentDirectory = storageManager.getAttachmentDirectory(uUid,
|
2010-12-18 17:56:40 -05:00
|
|
|
database.getStorageProviderId());
|
2010-11-13 16:40:56 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
return database.execute(false, new DbCallback<Long>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Long doDbWork(final SQLiteDatabase db) {
|
2010-11-13 16:40:56 -05:00
|
|
|
final File[] files = attachmentDirectory.listFiles();
|
|
|
|
long attachmentLength = 0;
|
2014-03-22 19:39:10 -04:00
|
|
|
if (files != null) {
|
|
|
|
for (File file : files) {
|
|
|
|
if (file.exists()) {
|
|
|
|
attachmentLength += file.length();
|
|
|
|
}
|
2010-11-13 16:40:56 -05:00
|
|
|
}
|
|
|
|
}
|
2010-02-17 22:28:31 -05:00
|
|
|
|
2010-12-18 17:56:40 -05:00
|
|
|
final File dbFile = storageManager.getDatabase(uUid, database.getStorageProviderId());
|
2010-11-13 16:40:56 -05:00
|
|
|
return dbFile.length() + attachmentLength;
|
|
|
|
}
|
|
|
|
});
|
2009-02-09 22:18:42 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void compact() throws MessagingException {
|
2010-04-21 22:20:35 -04:00
|
|
|
if (K9.DEBUG)
|
2010-12-01 01:04:28 -05:00
|
|
|
Log.i(K9.LOG_TAG, "Before compaction size = " + getSize());
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
database.execute(false, new DbCallback<Void>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
2010-11-13 16:40:56 -05:00
|
|
|
db.execSQL("VACUUM");
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
2010-04-21 22:20:35 -04:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "After compaction size = " + getSize());
|
2009-02-09 22:18:42 -05:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void clear() throws MessagingException {
|
2010-04-21 22:20:35 -04:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.i(K9.LOG_TAG, "Before prune size = " + getSize());
|
2009-11-24 19:40:29 -05:00
|
|
|
|
|
|
|
pruneCachedAttachments(true);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG) {
|
2010-04-21 22:20:35 -04:00
|
|
|
Log.i(K9.LOG_TAG, "After prune / before compaction size = " + getSize());
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-04-21 22:20:35 -04:00
|
|
|
Log.i(K9.LOG_TAG, "Before clear folder count = " + getFolderCount());
|
|
|
|
Log.i(K9.LOG_TAG, "Before clear message count = " + getMessageCount());
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-04-21 22:20:35 -04:00
|
|
|
Log.i(K9.LOG_TAG, "After prune / before clear size = " + getSize());
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
// don't delete messages that are Local, since there is no copy on the server.
|
|
|
|
// Don't delete deleted messages. They are essentially placeholders for UIDs of messages that have
|
2009-11-29 23:57:29 -05:00
|
|
|
// been deleted locally. They take up insignificant space
|
2011-02-06 17:09:48 -05:00
|
|
|
database.execute(false, new DbCallback<Void>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Void doDbWork(final SQLiteDatabase db) {
|
2013-01-10 21:40:35 -05:00
|
|
|
// Delete entries from 'threads' table
|
|
|
|
db.execSQL("DELETE FROM threads WHERE message_id IN " +
|
|
|
|
"(SELECT id FROM messages WHERE deleted = 0 AND uid NOT LIKE 'Local%')");
|
|
|
|
|
|
|
|
// Set 'root' and 'parent' of remaining entries in 'thread' table to 'NULL' to make
|
|
|
|
// sure the thread structure is in a valid state (this may destroy existing valid
|
|
|
|
// thread trees, but is much faster than adjusting the tree by removing messages
|
|
|
|
// one by one).
|
2013-03-07 19:15:26 -05:00
|
|
|
db.execSQL("UPDATE threads SET root=id, parent=NULL");
|
2013-01-10 21:40:35 -05:00
|
|
|
|
|
|
|
// Delete entries from 'messages' table
|
|
|
|
db.execSQL("DELETE FROM messages WHERE deleted = 0 AND uid NOT LIKE 'Local%'");
|
2010-11-13 16:40:56 -05:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
2009-11-24 19:40:29 -05:00
|
|
|
|
|
|
|
compact();
|
2010-11-13 16:40:56 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG) {
|
2010-04-21 22:20:35 -04:00
|
|
|
Log.i(K9.LOG_TAG, "After clear message count = " + getMessageCount());
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-04-21 22:20:35 -04:00
|
|
|
Log.i(K9.LOG_TAG, "After clear size = " + getSize());
|
|
|
|
}
|
2009-02-09 22:18:42 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getMessageCount() throws MessagingException {
|
|
|
|
return database.execute(false, new DbCallback<Integer>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Integer doDbWork(final SQLiteDatabase db) {
|
2010-11-13 16:40:56 -05:00
|
|
|
Cursor cursor = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-11-13 16:40:56 -05:00
|
|
|
cursor = db.rawQuery("SELECT COUNT(*) FROM messages", null);
|
|
|
|
cursor.moveToFirst();
|
2010-12-28 04:10:30 -05:00
|
|
|
return cursor.getInt(0); // message count
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
2011-10-20 02:25:27 -04:00
|
|
|
Utility.closeQuietly(cursor);
|
2010-11-13 16:40:56 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
2010-11-13 16:40:56 -05:00
|
|
|
});
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public int getFolderCount() throws MessagingException {
|
|
|
|
return database.execute(false, new DbCallback<Integer>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Integer doDbWork(final SQLiteDatabase db) {
|
2010-11-13 16:40:56 -05:00
|
|
|
Cursor cursor = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-11-13 16:40:56 -05:00
|
|
|
cursor = db.rawQuery("SELECT COUNT(*) FROM folders", null);
|
|
|
|
cursor.moveToFirst();
|
2010-12-28 04:10:30 -05:00
|
|
|
return cursor.getInt(0); // folder count
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
2011-10-20 02:25:27 -04:00
|
|
|
Utility.closeQuietly(cursor);
|
2010-11-13 16:40:56 -05:00
|
|
|
}
|
2009-02-09 22:18:42 -05:00
|
|
|
}
|
2010-11-13 16:40:56 -05:00
|
|
|
});
|
2009-02-09 22:18:42 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public LocalFolder getFolder(String name) {
|
2014-09-07 10:01:33 -04:00
|
|
|
return new LocalFolder(this, name);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2012-10-17 23:15:40 -04:00
|
|
|
public LocalFolder getFolderById(long folderId) {
|
2014-09-07 10:01:33 -04:00
|
|
|
return new LocalFolder(this, folderId);
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
2008-11-01 17:32:06 -04:00
|
|
|
// TODO this takes about 260-300ms, seems slow.
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public List <? extends Folder > getPersonalNamespaces(boolean forceListAll) throws MessagingException {
|
2010-11-13 16:40:56 -05:00
|
|
|
final List<LocalFolder> folders = new LinkedList<LocalFolder>();
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
|
|
|
database.execute(false, new DbCallback < List <? extends Folder >> () {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public List <? extends Folder > doDbWork(final SQLiteDatabase db) throws WrappedException {
|
2010-11-13 16:40:56 -05:00
|
|
|
Cursor cursor = null;
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2012-12-07 09:04:53 -05:00
|
|
|
cursor = db.rawQuery("SELECT " + GET_FOLDER_COLS + " FROM folders " +
|
2013-07-06 20:40:04 -04:00
|
|
|
"ORDER BY name ASC", null);
|
2011-02-06 17:09:48 -05:00
|
|
|
while (cursor.moveToNext()) {
|
2013-07-06 20:22:40 -04:00
|
|
|
if (cursor.isNull(FOLDER_ID_INDEX)) {
|
2012-12-10 11:34:48 -05:00
|
|
|
continue;
|
|
|
|
}
|
2013-07-06 20:22:40 -04:00
|
|
|
String folderName = cursor.getString(FOLDER_NAME_INDEX);
|
2014-09-07 10:01:33 -04:00
|
|
|
LocalFolder folder = new LocalFolder(LocalStore.this, folderName);
|
2013-07-06 20:22:40 -04:00
|
|
|
folder.open(cursor);
|
2010-11-13 16:40:56 -05:00
|
|
|
|
|
|
|
folders.add(folder);
|
|
|
|
}
|
|
|
|
return folders;
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (MessagingException e) {
|
2010-11-13 16:40:56 -05:00
|
|
|
throw new WrappedException(e);
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
2011-10-20 02:25:27 -04:00
|
|
|
Utility.closeQuietly(cursor);
|
2010-11-13 16:40:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (WrappedException e) {
|
|
|
|
throw(MessagingException) e.getCause();
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2010-04-21 22:20:35 -04:00
|
|
|
return folders;
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public void checkSettings() throws MessagingException {
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void delete() throws UnavailableStorageException {
|
2010-12-18 17:56:40 -05:00
|
|
|
database.delete();
|
2010-11-13 16:40:56 -05:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public void recreate() throws UnavailableStorageException {
|
2010-12-18 17:56:40 -05:00
|
|
|
database.recreate();
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2010-05-15 15:46:16 -04:00
|
|
|
|
2010-02-17 22:28:31 -05:00
|
|
|
/**
|
|
|
|
* Deletes all cached attachments for the entire store.
|
2010-12-28 04:10:50 -05:00
|
|
|
* @param force
|
|
|
|
* @throws com.fsck.k9.mail.MessagingException
|
2010-02-17 22:28:31 -05:00
|
|
|
*/
|
2014-09-22 15:52:59 -04:00
|
|
|
//TODO this method seems to be only called with force=true, simplify accordingly
|
2011-02-06 17:09:48 -05:00
|
|
|
private void pruneCachedAttachments(final boolean force) throws MessagingException {
|
|
|
|
database.execute(false, new DbCallback<Void>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
|
|
|
if (force) {
|
2010-11-13 16:40:56 -05:00
|
|
|
ContentValues cv = new ContentValues();
|
|
|
|
cv.putNull("content_uri");
|
|
|
|
db.update("attachments", cv, null, null);
|
|
|
|
}
|
2014-12-11 22:40:49 -05:00
|
|
|
final StorageManager storageManager = StorageManager.getInstance(context);
|
2010-12-18 17:56:40 -05:00
|
|
|
File[] files = storageManager.getAttachmentDirectory(uUid, database.getStorageProviderId()).listFiles();
|
2011-02-06 17:09:48 -05:00
|
|
|
for (File file : files) {
|
|
|
|
if (file.exists()) {
|
|
|
|
if (!force) {
|
2010-11-13 16:40:56 -05:00
|
|
|
Cursor cursor = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-11-13 16:40:56 -05:00
|
|
|
cursor = db.query(
|
|
|
|
"attachments",
|
|
|
|
new String[] { "store_data" },
|
|
|
|
"id = ?",
|
|
|
|
new String[] { file.getName() },
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
null);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (cursor.moveToNext()) {
|
|
|
|
if (cursor.getString(0) == null) {
|
2010-11-13 16:40:56 -05:00
|
|
|
if (K9.DEBUG)
|
|
|
|
Log.d(K9.LOG_TAG, "Attachment " + file.getAbsolutePath() + " has no store data, not deleting");
|
|
|
|
/*
|
|
|
|
* If the attachment has no store data it is not recoverable, so
|
|
|
|
* we won't delete it.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
2011-10-20 02:25:27 -04:00
|
|
|
Utility.closeQuietly(cursor);
|
2010-11-13 16:40:56 -05:00
|
|
|
}
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!force) {
|
|
|
|
try {
|
2010-11-13 16:40:56 -05:00
|
|
|
ContentValues cv = new ContentValues();
|
|
|
|
cv.putNull("content_uri");
|
|
|
|
db.update("attachments", cv, "id = ?", new String[] { file.getName() });
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2010-02-17 22:28:31 -05:00
|
|
|
/*
|
2010-11-13 16:40:56 -05:00
|
|
|
* If the row has gone away before we got to mark it not-downloaded that's
|
|
|
|
* okay.
|
2010-02-17 22:28:31 -05:00
|
|
|
*/
|
2010-02-11 16:16:37 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!file.delete()) {
|
2010-11-13 16:40:56 -05:00
|
|
|
file.deleteOnExit();
|
2010-02-11 16:16:37 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
}
|
2010-02-17 22:28:31 -05:00
|
|
|
}
|
2010-11-13 16:40:56 -05:00
|
|
|
return null;
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2010-11-13 16:40:56 -05:00
|
|
|
});
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2014-12-12 10:02:59 -05:00
|
|
|
public void resetVisibleLimits(int visibleLimit) throws MessagingException {
|
2010-11-13 16:40:56 -05:00
|
|
|
final ContentValues cv = new ContentValues();
|
2008-12-11 00:25:59 -05:00
|
|
|
cv.put("visible_limit", Integer.toString(visibleLimit));
|
2011-02-06 17:09:48 -05:00
|
|
|
database.execute(false, new DbCallback<Void>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
2010-11-13 16:40:56 -05:00
|
|
|
db.update("folders", cv, null, null);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2014-12-12 10:02:59 -05:00
|
|
|
public List<PendingCommand> getPendingCommands() throws MessagingException {
|
2014-10-04 06:45:45 -04:00
|
|
|
return database.execute(false, new DbCallback<List<PendingCommand>>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2014-10-04 06:45:45 -04:00
|
|
|
public List<PendingCommand> doDbWork(final SQLiteDatabase db) throws WrappedException {
|
2010-11-13 16:40:56 -05:00
|
|
|
Cursor cursor = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-11-13 16:40:56 -05:00
|
|
|
cursor = db.query("pending_commands",
|
|
|
|
new String[] { "id", "command", "arguments" },
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
"id ASC");
|
2014-10-04 06:45:45 -04:00
|
|
|
List<PendingCommand> commands = new ArrayList<PendingCommand>();
|
2011-02-06 17:09:48 -05:00
|
|
|
while (cursor.moveToNext()) {
|
2010-11-13 16:40:56 -05:00
|
|
|
PendingCommand command = new PendingCommand();
|
|
|
|
command.mId = cursor.getLong(0);
|
|
|
|
command.command = cursor.getString(1);
|
|
|
|
String arguments = cursor.getString(2);
|
|
|
|
command.arguments = arguments.split(",");
|
2011-02-06 17:09:48 -05:00
|
|
|
for (int i = 0; i < command.arguments.length; i++) {
|
2010-11-13 16:40:56 -05:00
|
|
|
command.arguments[i] = Utility.fastUrlDecode(command.arguments[i]);
|
|
|
|
}
|
|
|
|
commands.add(command);
|
|
|
|
}
|
|
|
|
return commands;
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
2011-10-20 02:25:27 -04:00
|
|
|
Utility.closeQuietly(cursor);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
}
|
2010-11-13 16:40:56 -05:00
|
|
|
});
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2014-12-12 10:02:59 -05:00
|
|
|
public void addPendingCommand(PendingCommand command) throws MessagingException {
|
2014-09-28 06:59:11 -04:00
|
|
|
for (int i = 0; i < command.arguments.length; i++) {
|
2014-09-29 13:06:21 -04:00
|
|
|
command.arguments[i] = UrlEncodingHelper.encodeUtf8(command.arguments[i]);
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
2014-09-28 06:59:11 -04:00
|
|
|
final ContentValues cv = new ContentValues();
|
|
|
|
cv.put("command", command.command);
|
|
|
|
cv.put("arguments", Utility.combine(command.arguments, ','));
|
|
|
|
database.execute(false, new DbCallback<Void>() {
|
|
|
|
@Override
|
|
|
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
|
|
|
db.insert("pending_commands", "command", cv);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2014-12-12 10:02:59 -05:00
|
|
|
public void removePendingCommand(final PendingCommand command) throws MessagingException {
|
2011-02-06 17:09:48 -05:00
|
|
|
database.execute(false, new DbCallback<Void>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
2010-11-13 16:40:56 -05:00
|
|
|
db.delete("pending_commands", "id = ?", new String[] { Long.toString(command.mId) });
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-12-12 10:02:59 -05:00
|
|
|
public void removePendingCommands() throws MessagingException {
|
2011-02-06 17:09:48 -05:00
|
|
|
database.execute(false, new DbCallback<Void>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
2010-11-13 16:40:56 -05:00
|
|
|
db.delete("pending_commands", null, null);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public static class PendingCommand {
|
2008-11-01 17:32:06 -04:00
|
|
|
private long mId;
|
|
|
|
public String command;
|
|
|
|
public String[] arguments;
|
|
|
|
|
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public String toString() {
|
2011-09-30 02:18:00 -04:00
|
|
|
StringBuilder sb = new StringBuilder();
|
2008-11-01 17:32:06 -04:00
|
|
|
sb.append(command);
|
Complete merge of DAmail functionality into K9mail. Following
features are added to K9mail:
1) Show unread message count on each folder
2) Sum unread count of all shown folders in an account to the account display
3) Periodically check selected folders for new mail, not just Inbox
4) Don't refresh folder when opened (unless folder is empty)
5) Show date and time of last sync for each folder
6) Fix timer for automatic periodic sync (use wakelock to assure completion)
7) Optimize local folder queries (speeds up account and folder lists)
8) Show Loading... message in status bar indicating which folder is being synced
9) Eliminate redundant sync of new messages (performance enhancement)
10) Improve notification text for multiple accounts
11) Do not automatically sync folders more often than the account-specific period
12) Use user-configured date and time formats
13) Select which folders are shown, using configurable Classes
14) Select which folders are synced, using configurable Classes
15) Added context (long press) menu to folders, to provide for Refresh
and Folder Settings
16) Status light flashes purple when there are unread messages
17) Folder list more quickly eliminates display of deleted and out-of-Class folders.
18) Delete works
19) Mark all messages as read (in the folder context menu)
20) Notifications only for new unread messages
21) One minute synchronization frequency
22) Deleting an unread message decrements unread counter
23) Notifications work for POP3 accounts
24) Message deletes work for POP3 accounts
25) Explicit errors show in folder list
26) Stack traces saved to folder K9mail-errors
27) Clear pending actions (danger, for emergencies only!)
28) Delete policy in Account settings
29) DNS cache in InetAddress disabled
30) Trapped some crash-causing error conditions
31) Eliminate duplicate copies to Sent folder
32) Prevent crashes due to message listener concurrency
33) Empty Trash
34) Nuclear "Mark all messages as read" (marks all messages as read in
server-side folder, irrespective of which messages have been downloaded)
35) Forward (alternate) to allow forwarding email through other programs
36) Accept text/plain Intents to allow other programs to send email through K9mail
37) Displays Outbox sending status
38) Manual retry of outbox sending when "Refresh"ing Outbox
39) Folder error status is persisted
40) Ability to log to arbitrary file
Fixes K9 issues 11, 23, 24, 65, 69, 71, 79, 81, 82, 83, 87, 101, 104,
107, 120, 148, 154
2008-12-30 22:49:09 -05:00
|
|
|
sb.append(": ");
|
2011-02-06 17:09:48 -05:00
|
|
|
for (String argument : arguments) {
|
2009-11-29 11:55:35 -05:00
|
|
|
sb.append(", ");
|
2008-11-01 17:32:06 -04:00
|
|
|
sb.append(argument);
|
Complete merge of DAmail functionality into K9mail. Following
features are added to K9mail:
1) Show unread message count on each folder
2) Sum unread count of all shown folders in an account to the account display
3) Periodically check selected folders for new mail, not just Inbox
4) Don't refresh folder when opened (unless folder is empty)
5) Show date and time of last sync for each folder
6) Fix timer for automatic periodic sync (use wakelock to assure completion)
7) Optimize local folder queries (speeds up account and folder lists)
8) Show Loading... message in status bar indicating which folder is being synced
9) Eliminate redundant sync of new messages (performance enhancement)
10) Improve notification text for multiple accounts
11) Do not automatically sync folders more often than the account-specific period
12) Use user-configured date and time formats
13) Select which folders are shown, using configurable Classes
14) Select which folders are synced, using configurable Classes
15) Added context (long press) menu to folders, to provide for Refresh
and Folder Settings
16) Status light flashes purple when there are unread messages
17) Folder list more quickly eliminates display of deleted and out-of-Class folders.
18) Delete works
19) Mark all messages as read (in the folder context menu)
20) Notifications only for new unread messages
21) One minute synchronization frequency
22) Deleting an unread message decrements unread counter
23) Notifications work for POP3 accounts
24) Message deletes work for POP3 accounts
25) Explicit errors show in folder list
26) Stack traces saved to folder K9mail-errors
27) Clear pending actions (danger, for emergencies only!)
28) Delete policy in Account settings
29) DNS cache in InetAddress disabled
30) Trapped some crash-causing error conditions
31) Eliminate duplicate copies to Sent folder
32) Prevent crashes due to message listener concurrency
33) Empty Trash
34) Nuclear "Mark all messages as read" (marks all messages as read in
server-side folder, irrespective of which messages have been downloaded)
35) Forward (alternate) to allow forwarding email through other programs
36) Accept text/plain Intents to allow other programs to send email through K9mail
37) Displays Outbox sending status
38) Manual retry of outbox sending when "Refresh"ing Outbox
39) Folder error status is persisted
40) Ability to log to arbitrary file
Fixes K9 issues 11, 23, 24, 65, 69, 71, 79, 81, 82, 83, 87, 101, 104,
107, 120, 148, 154
2008-12-30 22:49:09 -05:00
|
|
|
//sb.append("\n");
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|
|
|
|
return sb.toString();
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isMoveCapable() {
|
2009-11-24 19:40:29 -05:00
|
|
|
return true;
|
2009-03-05 02:32:45 -05:00
|
|
|
}
|
2009-12-27 11:53:37 -05:00
|
|
|
|
2010-04-16 08:20:10 -04:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public boolean isCopyCapable() {
|
2009-11-24 19:40:29 -05:00
|
|
|
return true;
|
2009-03-05 02:32:45 -05:00
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
|
2014-10-04 05:37:30 -04:00
|
|
|
public List<LocalMessage> searchForMessages(MessageRetrievalListener retrievalListener,
|
2012-10-16 16:42:51 -04:00
|
|
|
LocalSearch search) throws MessagingException {
|
|
|
|
|
2012-10-28 21:27:34 -04:00
|
|
|
StringBuilder query = new StringBuilder();
|
|
|
|
List<String> queryArgs = new ArrayList<String>();
|
|
|
|
SqlQueryBuilder.buildWhereClause(mAccount, search.getConditions(), query, queryArgs);
|
2012-10-16 16:42:51 -04:00
|
|
|
|
2012-11-02 20:52:45 -04:00
|
|
|
// Avoid "ambiguous column name" error by prefixing "id" with the message table name
|
|
|
|
String where = SqlQueryBuilder.addPrefixToSelection(new String[] { "id" },
|
|
|
|
"messages.", query.toString());
|
|
|
|
|
2012-10-28 21:27:34 -04:00
|
|
|
String[] selectionArgs = queryArgs.toArray(EMPTY_STRING_ARRAY);
|
2012-10-16 16:42:51 -04:00
|
|
|
|
2012-10-30 20:45:44 -04:00
|
|
|
String sqlQuery = "SELECT " + GET_MESSAGES_COLS + "FROM messages " +
|
2013-01-11 22:21:53 -05:00
|
|
|
"LEFT JOIN threads ON (threads.message_id = messages.id) " +
|
2012-11-01 15:33:13 -04:00
|
|
|
"LEFT JOIN folders ON (folders.id = messages.folder_id) WHERE " +
|
2012-10-24 00:01:26 -04:00
|
|
|
"((empty IS NULL OR empty != 1) AND deleted = 0)" +
|
2014-12-11 16:48:22 -05:00
|
|
|
((!TextUtils.isEmpty(where)) ? " AND (" + where + ")" : "") +
|
2012-10-28 21:27:34 -04:00
|
|
|
" ORDER BY date DESC";
|
2012-10-16 16:42:51 -04:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
if (K9.DEBUG) {
|
2012-10-13 08:53:00 -04:00
|
|
|
Log.d(K9.LOG_TAG, "Query = " + sqlQuery);
|
2010-04-21 22:20:35 -04:00
|
|
|
}
|
2012-10-16 16:42:51 -04:00
|
|
|
|
2012-10-28 21:27:34 -04:00
|
|
|
return getMessages(retrievalListener, null, sqlQuery, selectionArgs);
|
2012-10-16 16:42:51 -04:00
|
|
|
}
|
|
|
|
|
2009-12-27 11:53:37 -05:00
|
|
|
/*
|
|
|
|
* Given a query string, actually do the query for the messages and
|
|
|
|
* call the MessageRetrievalListener for each one
|
|
|
|
*/
|
2014-10-04 05:37:30 -04:00
|
|
|
List<LocalMessage> getMessages(
|
2010-11-13 16:40:56 -05:00
|
|
|
final MessageRetrievalListener listener,
|
|
|
|
final LocalFolder folder,
|
|
|
|
final String queryString, final String[] placeHolders
|
2011-02-06 17:09:48 -05:00
|
|
|
) throws MessagingException {
|
2014-10-04 06:45:45 -04:00
|
|
|
final List<LocalMessage> messages = new ArrayList<LocalMessage>();
|
2011-02-06 17:09:48 -05:00
|
|
|
final int j = database.execute(false, new DbCallback<Integer>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Integer doDbWork(final SQLiteDatabase db) throws WrappedException {
|
2010-11-13 16:40:56 -05:00
|
|
|
Cursor cursor = null;
|
|
|
|
int i = 0;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-11-13 16:40:56 -05:00
|
|
|
cursor = db.rawQuery(queryString + " LIMIT 10", placeHolders);
|
2009-12-27 11:53:37 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
while (cursor.moveToNext()) {
|
2014-09-07 10:01:33 -04:00
|
|
|
LocalMessage message = new LocalMessage(LocalStore.this, null, folder);
|
2010-11-13 16:40:56 -05:00
|
|
|
message.populateFromGetMessageCursor(cursor);
|
|
|
|
|
|
|
|
messages.add(message);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2010-11-13 16:40:56 -05:00
|
|
|
listener.messageFinished(message, i, -1);
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
cursor.close();
|
|
|
|
cursor = db.rawQuery(queryString + " LIMIT -1 OFFSET 10", placeHolders);
|
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
while (cursor.moveToNext()) {
|
2014-09-07 10:01:33 -04:00
|
|
|
LocalMessage message = new LocalMessage(LocalStore.this, null, folder);
|
2010-11-13 16:40:56 -05:00
|
|
|
message.populateFromGetMessageCursor(cursor);
|
|
|
|
|
|
|
|
messages.add(message);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2010-11-13 16:40:56 -05:00
|
|
|
listener.messageFinished(message, i, -1);
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2011-02-06 17:09:48 -05:00
|
|
|
} catch (Exception e) {
|
2011-11-03 01:18:30 -04:00
|
|
|
Log.d(K9.LOG_TAG, "Got an exception", e);
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
2011-10-20 02:25:27 -04:00
|
|
|
Utility.closeQuietly(cursor);
|
2009-12-27 11:53:37 -05:00
|
|
|
}
|
2010-11-13 16:40:56 -05:00
|
|
|
return i;
|
2009-12-27 11:53:37 -05:00
|
|
|
}
|
2010-11-13 16:40:56 -05:00
|
|
|
});
|
2011-02-06 17:09:48 -05:00
|
|
|
if (listener != null) {
|
2010-11-13 16:40:56 -05:00
|
|
|
listener.messagesFinished(j);
|
|
|
|
}
|
2010-10-21 16:48:28 -04:00
|
|
|
|
2014-10-04 05:37:30 -04:00
|
|
|
return Collections.unmodifiableList(messages);
|
2010-11-13 16:40:56 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-10-04 05:37:30 -04:00
|
|
|
public List<LocalMessage> getMessagesInThread(final long rootId) throws MessagingException {
|
2012-11-02 20:52:45 -04:00
|
|
|
String rootIdString = Long.toString(rootId);
|
|
|
|
|
|
|
|
LocalSearch search = new LocalSearch();
|
2015-03-06 15:17:49 -05:00
|
|
|
search.and(SearchField.THREAD_ID, rootIdString, Attribute.EQUALS);
|
2012-11-02 20:52:45 -04:00
|
|
|
|
|
|
|
return searchForMessages(null, search);
|
|
|
|
}
|
|
|
|
|
2014-12-12 10:02:59 -05:00
|
|
|
public AttachmentInfo getAttachmentInfo(final String attachmentId) throws MessagingException {
|
2011-02-06 17:09:48 -05:00
|
|
|
return database.execute(false, new DbCallback<AttachmentInfo>() {
|
2010-11-13 16:40:56 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public AttachmentInfo doDbWork(final SQLiteDatabase db) throws WrappedException {
|
2010-12-28 04:10:50 -05:00
|
|
|
String name;
|
2011-03-24 18:07:46 -04:00
|
|
|
String type;
|
2010-12-28 04:10:50 -05:00
|
|
|
int size;
|
2010-11-13 16:40:56 -05:00
|
|
|
Cursor cursor = null;
|
2011-02-06 17:09:48 -05:00
|
|
|
try {
|
2010-11-13 16:40:56 -05:00
|
|
|
cursor = db.query(
|
|
|
|
"attachments",
|
2011-03-24 18:07:46 -04:00
|
|
|
new String[] { "name", "size", "mime_type" },
|
2010-11-13 16:40:56 -05:00
|
|
|
"id = ?",
|
|
|
|
new String[] { attachmentId },
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
null);
|
2011-02-06 17:09:48 -05:00
|
|
|
if (!cursor.moveToFirst()) {
|
2010-11-13 16:40:56 -05:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
name = cursor.getString(0);
|
|
|
|
size = cursor.getInt(1);
|
2011-03-24 18:07:46 -04:00
|
|
|
type = cursor.getString(2);
|
2010-11-13 16:40:56 -05:00
|
|
|
final AttachmentInfo attachmentInfo = new AttachmentInfo();
|
|
|
|
attachmentInfo.name = name;
|
|
|
|
attachmentInfo.size = size;
|
2011-03-24 18:07:46 -04:00
|
|
|
attachmentInfo.type = type;
|
2010-11-13 16:40:56 -05:00
|
|
|
return attachmentInfo;
|
2011-02-06 17:09:48 -05:00
|
|
|
} finally {
|
2011-10-20 02:25:27 -04:00
|
|
|
Utility.closeQuietly(cursor);
|
2010-11-13 16:40:56 -05:00
|
|
|
}
|
2009-12-27 11:53:37 -05:00
|
|
|
}
|
2010-11-13 16:40:56 -05:00
|
|
|
});
|
2009-12-27 11:53:37 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2011-02-06 17:09:48 -05:00
|
|
|
public static class AttachmentInfo {
|
2010-11-13 16:40:56 -05:00
|
|
|
public String name;
|
|
|
|
public int size;
|
2011-03-24 18:07:46 -04:00
|
|
|
public String type;
|
2010-11-13 16:40:56 -05:00
|
|
|
}
|
2009-11-24 19:40:29 -05:00
|
|
|
|
2014-12-12 10:02:59 -05:00
|
|
|
public void createFolders(final List<LocalFolder> foldersToCreate, final int visibleLimit) throws MessagingException {
|
2011-02-06 17:09:48 -05:00
|
|
|
database.execute(true, new DbCallback<Void>() {
|
2011-02-04 07:26:14 -05:00
|
|
|
@Override
|
2011-02-06 17:09:48 -05:00
|
|
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException {
|
|
|
|
for (LocalFolder folder : foldersToCreate) {
|
2011-02-04 07:26:14 -05:00
|
|
|
String name = folder.getName();
|
|
|
|
final LocalFolder.PreferencesHolder prefHolder = folder.new PreferencesHolder();
|
|
|
|
|
|
|
|
// When created, special folders should always be displayed
|
|
|
|
// inbox should be integrated
|
|
|
|
// and the inbox and drafts folders should be syncced by default
|
2011-02-06 17:09:48 -05:00
|
|
|
if (mAccount.isSpecialFolder(name)) {
|
2011-02-04 07:26:14 -05:00
|
|
|
prefHolder.inTopGroup = true;
|
|
|
|
prefHolder.displayClass = LocalFolder.FolderClass.FIRST_CLASS;
|
2011-04-05 05:27:39 -04:00
|
|
|
if (name.equalsIgnoreCase(mAccount.getInboxFolderName())) {
|
2011-02-04 07:26:14 -05:00
|
|
|
prefHolder.integrate = true;
|
2014-08-06 05:04:07 -04:00
|
|
|
prefHolder.notifyClass = LocalFolder.FolderClass.FIRST_CLASS;
|
2011-02-04 07:26:14 -05:00
|
|
|
prefHolder.pushClass = LocalFolder.FolderClass.FIRST_CLASS;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2011-02-04 07:26:14 -05:00
|
|
|
prefHolder.pushClass = LocalFolder.FolderClass.INHERITED;
|
|
|
|
|
|
|
|
}
|
2011-04-05 05:27:39 -04:00
|
|
|
if (name.equalsIgnoreCase(mAccount.getInboxFolderName()) ||
|
2011-02-06 17:09:48 -05:00
|
|
|
name.equalsIgnoreCase(mAccount.getDraftsFolderName())) {
|
2011-02-04 07:26:14 -05:00
|
|
|
prefHolder.syncClass = LocalFolder.FolderClass.FIRST_CLASS;
|
2011-02-06 17:09:48 -05:00
|
|
|
} else {
|
2011-02-04 07:26:14 -05:00
|
|
|
prefHolder.syncClass = LocalFolder.FolderClass.NO_CLASS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
folder.refresh(name, prefHolder); // Recover settings from Preferences
|
|
|
|
|
2014-08-06 05:04:07 -04:00
|
|
|
db.execSQL("INSERT INTO folders (name, visible_limit, top_group, display_class, poll_class, notify_class, push_class, integrate) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", new Object[] {
|
2011-02-06 17:09:48 -05:00
|
|
|
name,
|
2011-02-04 07:26:14 -05:00
|
|
|
visibleLimit,
|
|
|
|
prefHolder.inTopGroup ? 1 : 0,
|
|
|
|
prefHolder.displayClass.name(),
|
|
|
|
prefHolder.syncClass.name(),
|
2014-08-06 05:04:07 -04:00
|
|
|
prefHolder.notifyClass.name(),
|
2011-02-04 07:26:14 -05:00
|
|
|
prefHolder.pushClass.name(),
|
|
|
|
prefHolder.integrate ? 1 : 0,
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2012-12-03 23:13:58 -05:00
|
|
|
|
2014-10-04 05:37:30 -04:00
|
|
|
String serializeFlags(Iterable<Flag> flags) {
|
2012-12-03 23:13:58 -05:00
|
|
|
List<Flag> extraFlags = new ArrayList<Flag>();
|
|
|
|
|
|
|
|
for (Flag flag : flags) {
|
|
|
|
switch (flag) {
|
|
|
|
case DELETED:
|
|
|
|
case SEEN:
|
|
|
|
case FLAGGED:
|
|
|
|
case ANSWERED:
|
|
|
|
case FORWARDED: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
extraFlags.add(flag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-04 05:37:30 -04:00
|
|
|
return Utility.combine(extraFlags, ',').toUpperCase(Locale.US);
|
2012-12-03 23:13:58 -05:00
|
|
|
}
|
|
|
|
|
2014-12-11 18:56:02 -05:00
|
|
|
// TODO: database should not be exposed!
|
2012-10-16 09:46:40 -04:00
|
|
|
public LockableDatabase getDatabase() {
|
|
|
|
return database;
|
|
|
|
}
|
2012-10-23 18:08:44 -04:00
|
|
|
|
2014-09-07 10:01:33 -04:00
|
|
|
void notifyChange() {
|
2012-10-23 18:08:44 -04:00
|
|
|
Uri uri = Uri.withAppendedPath(EmailProvider.CONTENT_URI, "account/" + uUid + "/messages");
|
|
|
|
mContentResolver.notifyChange(uri, null);
|
|
|
|
}
|
2012-12-05 23:25:41 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Split database operations with a large set of arguments into multiple SQL statements.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* At the time of this writing (2012-12-06) SQLite only supports around 1000 arguments. That's
|
|
|
|
* why we have to split SQL statements with a large set of arguments into multiple SQL
|
|
|
|
* statements each working on a subset of the arguments.
|
|
|
|
* </p>
|
|
|
|
*
|
|
|
|
* @param selectionCallback
|
|
|
|
* Supplies the argument set and the code to query/update the database.
|
|
|
|
* @param batchSize
|
|
|
|
* The maximum size of the selection set in each SQL statement.
|
|
|
|
*
|
|
|
|
* @throws MessagingException
|
|
|
|
*/
|
|
|
|
public void doBatchSetSelection(final BatchSetSelection selectionCallback, final int batchSize)
|
|
|
|
throws MessagingException {
|
|
|
|
|
|
|
|
final List<String> selectionArgs = new ArrayList<String>();
|
|
|
|
int start = 0;
|
|
|
|
|
|
|
|
while (start < selectionCallback.getListSize()) {
|
|
|
|
final StringBuilder selection = new StringBuilder();
|
|
|
|
|
|
|
|
selection.append(" IN (");
|
|
|
|
|
|
|
|
int count = Math.min(selectionCallback.getListSize() - start, batchSize);
|
|
|
|
|
|
|
|
for (int i = start, end = start + count; i < end; i++) {
|
|
|
|
if (i > start) {
|
|
|
|
selection.append(",?");
|
|
|
|
} else {
|
|
|
|
selection.append("?");
|
|
|
|
}
|
|
|
|
|
|
|
|
selectionArgs.add(selectionCallback.getListItem(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
selection.append(")");
|
|
|
|
|
|
|
|
try {
|
|
|
|
database.execute(true, new DbCallback<Void>() {
|
|
|
|
@Override
|
|
|
|
public Void doDbWork(final SQLiteDatabase db) throws WrappedException,
|
|
|
|
UnavailableStorageException {
|
|
|
|
|
|
|
|
selectionCallback.doDbWork(db, selection.toString(),
|
|
|
|
selectionArgs.toArray(EMPTY_STRING_ARRAY));
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
selectionCallback.postDbWork();
|
|
|
|
|
|
|
|
} catch (WrappedException e) {
|
|
|
|
throw(MessagingException) e.getCause();
|
|
|
|
}
|
|
|
|
|
|
|
|
selectionArgs.clear();
|
|
|
|
start += count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Defines the behavior of {@link LocalStore#doBatchSetSelection(BatchSetSelection, int)}.
|
|
|
|
*/
|
|
|
|
public interface BatchSetSelection {
|
|
|
|
/**
|
|
|
|
* @return The size of the argument list.
|
|
|
|
*/
|
|
|
|
int getListSize();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a specific item of the argument list.
|
|
|
|
*
|
|
|
|
* @param index
|
|
|
|
* The index of the item.
|
|
|
|
*
|
|
|
|
* @return Item at position {@code i} of the argument list.
|
|
|
|
*/
|
|
|
|
String getListItem(int index);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Execute the SQL statement.
|
|
|
|
*
|
|
|
|
* @param db
|
|
|
|
* Use this {@link SQLiteDatabase} instance for your SQL statement.
|
|
|
|
* @param selectionSet
|
|
|
|
* A partial selection string containing place holders for the argument list, e.g.
|
|
|
|
* {@code " IN (?,?,?)"} (starts with a space).
|
|
|
|
* @param selectionArgs
|
|
|
|
* The current subset of the argument list.
|
|
|
|
* @throws UnavailableStorageException
|
|
|
|
*/
|
|
|
|
void doDbWork(SQLiteDatabase db, String selectionSet, String[] selectionArgs)
|
|
|
|
throws UnavailableStorageException;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This will be executed after each invocation of
|
|
|
|
* {@link #doDbWork(SQLiteDatabase, String, String[])} (after the transaction has been
|
|
|
|
* committed).
|
|
|
|
*/
|
|
|
|
void postDbWork();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Change the state of a flag for a list of messages.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* The goal of this method is to be fast. Currently this means using as few SQL UPDATE
|
2013-01-11 20:28:12 -05:00
|
|
|
* statements as possible.
|
2012-12-05 23:25:41 -05:00
|
|
|
*
|
|
|
|
* @param messageIds
|
|
|
|
* A list of primary keys in the "messages" table.
|
|
|
|
* @param flag
|
|
|
|
* The flag to change. This must be a flag with a separate column in the database.
|
|
|
|
* @param newState
|
|
|
|
* {@code true}, if the flag should be set. {@code false}, otherwise.
|
|
|
|
*
|
|
|
|
* @throws MessagingException
|
|
|
|
*/
|
2013-01-10 21:40:35 -05:00
|
|
|
public void setFlag(final List<Long> messageIds, final Flag flag, final boolean newState)
|
|
|
|
throws MessagingException {
|
2012-12-05 23:25:41 -05:00
|
|
|
|
|
|
|
final ContentValues cv = new ContentValues();
|
2013-02-18 22:45:14 -05:00
|
|
|
cv.put(getColumnNameForFlag(flag), newState);
|
2012-12-05 23:25:41 -05:00
|
|
|
|
|
|
|
doBatchSetSelection(new BatchSetSelection() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getListSize() {
|
|
|
|
return messageIds.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getListItem(int index) {
|
|
|
|
return Long.toString(messageIds.get(index));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void doDbWork(SQLiteDatabase db, String selectionSet, String[] selectionArgs)
|
|
|
|
throws UnavailableStorageException {
|
|
|
|
|
|
|
|
db.update("messages", cv, "(empty IS NULL OR empty != 1) AND id" + selectionSet,
|
|
|
|
selectionArgs);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void postDbWork() {
|
|
|
|
notifyChange();
|
|
|
|
}
|
|
|
|
}, FLAG_UPDATE_BATCH_SIZE);
|
|
|
|
}
|
|
|
|
|
2013-01-11 20:28:12 -05:00
|
|
|
/**
|
|
|
|
* Change the state of a flag for a list of threads.
|
|
|
|
*
|
|
|
|
* <p>
|
|
|
|
* The goal of this method is to be fast. Currently this means using as few SQL UPDATE
|
|
|
|
* statements as possible.
|
|
|
|
*
|
|
|
|
* @param threadRootIds
|
|
|
|
* A list of root thread IDs.
|
|
|
|
* @param flag
|
|
|
|
* The flag to change. This must be a flag with a separate column in the database.
|
|
|
|
* @param newState
|
|
|
|
* {@code true}, if the flag should be set. {@code false}, otherwise.
|
|
|
|
*
|
|
|
|
* @throws MessagingException
|
|
|
|
*/
|
|
|
|
public void setFlagForThreads(final List<Long> threadRootIds, Flag flag, final boolean newState)
|
2013-01-10 21:40:35 -05:00
|
|
|
throws MessagingException {
|
|
|
|
|
2013-02-18 22:45:14 -05:00
|
|
|
final String flagColumn = getColumnNameForFlag(flag);
|
2013-01-10 21:40:35 -05:00
|
|
|
|
|
|
|
doBatchSetSelection(new BatchSetSelection() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getListSize() {
|
2013-01-11 20:28:12 -05:00
|
|
|
return threadRootIds.size();
|
2013-01-10 21:40:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getListItem(int index) {
|
2013-01-11 20:28:12 -05:00
|
|
|
return Long.toString(threadRootIds.get(index));
|
2013-01-10 21:40:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void doDbWork(SQLiteDatabase db, String selectionSet, String[] selectionArgs)
|
|
|
|
throws UnavailableStorageException {
|
|
|
|
|
|
|
|
db.execSQL("UPDATE messages SET " + flagColumn + " = " + ((newState) ? "1" : "0") +
|
|
|
|
" WHERE id IN (" +
|
2013-01-11 20:28:12 -05:00
|
|
|
"SELECT m.id FROM threads t " +
|
|
|
|
"LEFT JOIN messages m ON (t.message_id = m.id) " +
|
2013-01-10 21:40:35 -05:00
|
|
|
"WHERE (m.empty IS NULL OR m.empty != 1) AND m.deleted = 0 " +
|
2013-03-07 19:15:26 -05:00
|
|
|
"AND t.root" + selectionSet + ")",
|
|
|
|
selectionArgs);
|
2012-12-05 23:25:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void postDbWork() {
|
|
|
|
notifyChange();
|
|
|
|
}
|
2013-01-11 20:28:12 -05:00
|
|
|
}, THREAD_FLAG_UPDATE_BATCH_SIZE);
|
2012-12-05 23:25:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get folder name and UID for the supplied messages.
|
|
|
|
*
|
|
|
|
* @param messageIds
|
|
|
|
* A list of primary keys in the "messages" table.
|
|
|
|
* @param threadedList
|
2013-01-11 22:02:21 -05:00
|
|
|
* If this is {@code true}, {@code messageIds} contains the thread IDs of the messages
|
|
|
|
* at the root of a thread. In that case return UIDs for all messages in these threads.
|
2012-12-05 23:25:41 -05:00
|
|
|
* If this is {@code false} only the UIDs for messages in {@code messageIds} are
|
|
|
|
* returned.
|
|
|
|
*
|
|
|
|
* @return The list of UIDs for the messages grouped by folder name.
|
|
|
|
*
|
|
|
|
* @throws MessagingException
|
|
|
|
*/
|
|
|
|
public Map<String, List<String>> getFoldersAndUids(final List<Long> messageIds,
|
|
|
|
final boolean threadedList) throws MessagingException {
|
|
|
|
|
|
|
|
final Map<String, List<String>> folderMap = new HashMap<String, List<String>>();
|
|
|
|
|
|
|
|
doBatchSetSelection(new BatchSetSelection() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getListSize() {
|
|
|
|
return messageIds.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getListItem(int index) {
|
|
|
|
return Long.toString(messageIds.get(index));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void doDbWork(SQLiteDatabase db, String selectionSet, String[] selectionArgs)
|
|
|
|
throws UnavailableStorageException {
|
|
|
|
|
|
|
|
if (threadedList) {
|
2013-01-10 21:40:35 -05:00
|
|
|
String sql = "SELECT m.uid, f.name " +
|
2013-01-11 22:02:21 -05:00
|
|
|
"FROM threads t " +
|
|
|
|
"LEFT JOIN messages m ON (t.message_id = m.id) " +
|
|
|
|
"LEFT JOIN folders f ON (m.folder_id = f.id) " +
|
2013-01-10 21:40:35 -05:00
|
|
|
"WHERE (m.empty IS NULL OR m.empty != 1) AND m.deleted = 0 " +
|
2013-03-07 19:15:26 -05:00
|
|
|
"AND t.root" + selectionSet;
|
2012-12-05 23:25:41 -05:00
|
|
|
|
2013-03-07 19:15:26 -05:00
|
|
|
getDataFromCursor(db.rawQuery(sql, selectionArgs));
|
2013-01-10 21:40:35 -05:00
|
|
|
|
|
|
|
} else {
|
2013-01-11 22:02:21 -05:00
|
|
|
String sql =
|
2013-01-10 21:40:35 -05:00
|
|
|
"SELECT m.uid, f.name " +
|
|
|
|
"FROM messages m " +
|
2013-01-11 22:02:21 -05:00
|
|
|
"LEFT JOIN folders f ON (m.folder_id = f.id) " +
|
|
|
|
"WHERE (m.empty IS NULL OR m.empty != 1) AND m.id" + selectionSet;
|
2013-01-10 21:40:35 -05:00
|
|
|
|
|
|
|
getDataFromCursor(db.rawQuery(sql, selectionArgs));
|
2012-12-05 23:25:41 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void getDataFromCursor(Cursor cursor) {
|
|
|
|
try {
|
|
|
|
while (cursor.moveToNext()) {
|
|
|
|
String uid = cursor.getString(0);
|
|
|
|
String folderName = cursor.getString(1);
|
|
|
|
|
|
|
|
List<String> uidList = folderMap.get(folderName);
|
|
|
|
if (uidList == null) {
|
|
|
|
uidList = new ArrayList<String>();
|
|
|
|
folderMap.put(folderName, uidList);
|
|
|
|
}
|
|
|
|
|
|
|
|
uidList.add(uid);
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
cursor.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void postDbWork() {
|
|
|
|
notifyChange();
|
|
|
|
|
|
|
|
}
|
|
|
|
}, UID_CHECK_BATCH_SIZE);
|
|
|
|
|
|
|
|
return folderMap;
|
|
|
|
}
|
2008-11-01 17:32:06 -04:00
|
|
|
}
|