mirror of
https://github.com/moparisthebest/k-9
synced 2024-12-25 00:58:50 -05:00
Use separate table to store the thread structure
This commit is contained in:
parent
3f84bb54f2
commit
1df88ea153
@ -711,8 +711,7 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
|
||||
public void showThread(Account account, String folderName, long threadRootId) {
|
||||
LocalSearch tmpSearch = new LocalSearch();
|
||||
tmpSearch.addAccountUuid(account.getUuid());
|
||||
tmpSearch.and(Searchfield.THREAD_ROOT, String.valueOf(threadRootId), Attribute.EQUALS);
|
||||
tmpSearch.or(new SearchCondition(Searchfield.ID, Attribute.EQUALS, String.valueOf(threadRootId)));
|
||||
tmpSearch.and(Searchfield.THREAD_ID, String.valueOf(threadRootId), Attribute.EQUALS);
|
||||
|
||||
MessageListFragment fragment = MessageListFragment.newInstance(tmpSearch, true, false);
|
||||
addMessageListFragment(fragment, true);
|
||||
|
@ -3758,8 +3758,9 @@ public class MessagingController implements Runnable {
|
||||
|
||||
List<Message> messagesInThreads = new ArrayList<Message>();
|
||||
for (Message message : messages) {
|
||||
long rootId = ((LocalMessage) message).getRootId();
|
||||
long threadId = (rootId == -1) ? message.getId() : rootId;
|
||||
LocalMessage localMessage = (LocalMessage) message;
|
||||
long rootId = localMessage.getRootId();
|
||||
long threadId = (rootId == -1) ? localMessage.getThreadId() : rootId;
|
||||
|
||||
Message[] messagesInThread = localStore.getMessagesInThread(threadId);
|
||||
Collections.addAll(messagesInThreads, messagesInThread);
|
||||
|
@ -91,8 +91,12 @@ import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
||||
import com.fsck.k9.provider.EmailProvider;
|
||||
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
||||
import com.fsck.k9.provider.EmailProvider.SpecialColumns;
|
||||
import com.fsck.k9.provider.EmailProvider.ThreadColumns;
|
||||
import com.fsck.k9.search.ConditionsTreeNode;
|
||||
import com.fsck.k9.search.LocalSearch;
|
||||
import com.fsck.k9.search.SearchSpecification;
|
||||
import com.fsck.k9.search.SearchSpecification.SearchCondition;
|
||||
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
||||
import com.fsck.k9.search.SqlQueryBuilder;
|
||||
import com.handmark.pulltorefresh.library.PullToRefreshBase;
|
||||
import com.handmark.pulltorefresh.library.PullToRefreshListView;
|
||||
@ -117,11 +121,11 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
|
||||
MessageColumns.ATTACHMENT_COUNT,
|
||||
MessageColumns.FOLDER_ID,
|
||||
MessageColumns.PREVIEW,
|
||||
MessageColumns.THREAD_ROOT,
|
||||
ThreadColumns.ROOT,
|
||||
SpecialColumns.ACCOUNT_UUID,
|
||||
SpecialColumns.FOLDER_NAME,
|
||||
|
||||
MessageColumns.THREAD_COUNT,
|
||||
SpecialColumns.THREAD_COUNT,
|
||||
};
|
||||
|
||||
private static final int ID_COLUMN = 0;
|
||||
@ -2083,15 +2087,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
|
||||
accountMapping.put(account, messageIdList);
|
||||
}
|
||||
|
||||
long selectionId;
|
||||
if (mThreadedList) {
|
||||
selectionId = (cursor.isNull(THREAD_ROOT_COLUMN)) ?
|
||||
cursor.getLong(ID_COLUMN) : cursor.getLong(THREAD_ROOT_COLUMN);
|
||||
} else {
|
||||
selectionId = cursor.getLong(ID_COLUMN);
|
||||
}
|
||||
|
||||
messageIdList.add(selectionId);
|
||||
messageIdList.add(cursor.getLong(ID_COLUMN));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2888,19 +2884,30 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
|
||||
String accountUuid = mAccountUuids[id];
|
||||
Account account = mPreferences.getAccount(accountUuid);
|
||||
|
||||
String threadId = getThreadId(mSearch);
|
||||
|
||||
Uri uri;
|
||||
String[] projection;
|
||||
if (mThreadedList) {
|
||||
boolean needConditions;
|
||||
if (threadId != null) {
|
||||
uri = Uri.withAppendedPath(EmailProvider.CONTENT_URI, "account/" + accountUuid + "/thread/" + threadId);
|
||||
projection = PROJECTION;
|
||||
needConditions = false;
|
||||
} else if (mThreadedList) {
|
||||
uri = Uri.withAppendedPath(EmailProvider.CONTENT_URI, "account/" + accountUuid + "/messages/threaded");
|
||||
projection = THREADED_PROJECTION;
|
||||
needConditions = true;
|
||||
} else {
|
||||
uri = Uri.withAppendedPath(EmailProvider.CONTENT_URI, "account/" + accountUuid + "/messages");
|
||||
projection = PROJECTION;
|
||||
needConditions = true;
|
||||
}
|
||||
|
||||
StringBuilder query = new StringBuilder();
|
||||
List<String> queryArgs = new ArrayList<String>();
|
||||
SqlQueryBuilder.buildWhereClause(account, mSearch.getConditions(), query, queryArgs);
|
||||
if (needConditions) {
|
||||
SqlQueryBuilder.buildWhereClause(account, mSearch.getConditions(), query, queryArgs);
|
||||
}
|
||||
|
||||
String selection = query.toString();
|
||||
String[] selectionArgs = queryArgs.toArray(new String[0]);
|
||||
@ -2911,6 +2918,17 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
|
||||
sortOrder);
|
||||
}
|
||||
|
||||
private String getThreadId(LocalSearch search) {
|
||||
for (ConditionsTreeNode node : search.getLeafSet()) {
|
||||
SearchCondition condition = node.mCondition;
|
||||
if (condition.field == Searchfield.THREAD_ID) {
|
||||
return condition.value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String buildSortOrder() {
|
||||
String sortColumn = MessageColumns.ID;
|
||||
switch (mSortType) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -54,7 +54,7 @@ public class EmailProvider extends ContentProvider {
|
||||
private static final int MESSAGE_BASE = 0;
|
||||
private static final int MESSAGES = MESSAGE_BASE;
|
||||
private static final int MESSAGES_THREADED = MESSAGE_BASE + 1;
|
||||
//private static final int MESSAGES_THREAD = MESSAGE_BASE + 2;
|
||||
private static final int MESSAGES_THREAD = MESSAGE_BASE + 2;
|
||||
|
||||
private static final int STATS_BASE = 100;
|
||||
private static final int STATS = STATS_BASE;
|
||||
@ -78,8 +78,6 @@ public class EmailProvider extends ContentProvider {
|
||||
MessageColumns.ATTACHMENT_COUNT,
|
||||
MessageColumns.FOLDER_ID,
|
||||
MessageColumns.PREVIEW,
|
||||
MessageColumns.THREAD_ROOT,
|
||||
MessageColumns.THREAD_PARENT,
|
||||
MessageColumns.READ,
|
||||
MessageColumns.FLAGGED,
|
||||
MessageColumns.ANSWERED,
|
||||
@ -95,6 +93,8 @@ public class EmailProvider extends ContentProvider {
|
||||
MessageColumns.ID
|
||||
};
|
||||
|
||||
private static final String FOLDERS_TABLE = "folders";
|
||||
|
||||
private static final String[] FOLDERS_COLUMNS = {
|
||||
FolderColumns.ID,
|
||||
FolderColumns.NAME,
|
||||
@ -112,12 +112,21 @@ public class EmailProvider extends ContentProvider {
|
||||
FolderColumns.DISPLAY_CLASS
|
||||
};
|
||||
|
||||
private static final String THREADS_TABLE = "threads";
|
||||
|
||||
private static final String[] THREADS_COLUMNS = {
|
||||
ThreadColumns.ID,
|
||||
ThreadColumns.MESSAGE_ID,
|
||||
ThreadColumns.ROOT,
|
||||
ThreadColumns.PARENT
|
||||
};
|
||||
|
||||
static {
|
||||
UriMatcher matcher = sUriMatcher;
|
||||
|
||||
matcher.addURI(AUTHORITY, "account/*/messages", MESSAGES);
|
||||
matcher.addURI(AUTHORITY, "account/*/messages/threaded", MESSAGES_THREADED);
|
||||
//matcher.addURI(AUTHORITY, "account/*/thread/#", MESSAGES_THREAD);
|
||||
matcher.addURI(AUTHORITY, "account/*/thread/#", MESSAGES_THREAD);
|
||||
|
||||
matcher.addURI(AUTHORITY, "account/*/stats", STATS);
|
||||
}
|
||||
@ -125,6 +134,8 @@ public class EmailProvider extends ContentProvider {
|
||||
public interface SpecialColumns {
|
||||
public static final String ACCOUNT_UUID = "account_uuid";
|
||||
|
||||
public static final String THREAD_COUNT = "thread_count";
|
||||
|
||||
public static final String FOLDER_NAME = "name";
|
||||
public static final String INTEGRATE = "integrate";
|
||||
}
|
||||
@ -145,9 +156,6 @@ public class EmailProvider extends ContentProvider {
|
||||
public static final String ATTACHMENT_COUNT = "attachment_count";
|
||||
public static final String FOLDER_ID = "folder_id";
|
||||
public static final String PREVIEW = "preview";
|
||||
public static final String THREAD_ROOT = "thread_root";
|
||||
public static final String THREAD_PARENT = "thread_parent";
|
||||
public static final String THREAD_COUNT = "thread_count";
|
||||
public static final String READ = "read";
|
||||
public static final String FLAGGED = "flagged";
|
||||
public static final String ANSWERED = "answered";
|
||||
@ -179,6 +187,13 @@ public class EmailProvider extends ContentProvider {
|
||||
public static final String DISPLAY_CLASS = "display_class";
|
||||
}
|
||||
|
||||
public interface ThreadColumns {
|
||||
public static final String ID = "id";
|
||||
public static final String MESSAGE_ID = "message_id";
|
||||
public static final String ROOT = "root";
|
||||
public static final String PARENT = "parent";
|
||||
}
|
||||
|
||||
public interface StatsColumns {
|
||||
public static final String UNREAD_COUNT = "unread_count";
|
||||
public static final String FLAGGED_COUNT = "flagged_count";
|
||||
@ -216,7 +231,8 @@ public class EmailProvider extends ContentProvider {
|
||||
Cursor cursor = null;
|
||||
switch (match) {
|
||||
case MESSAGES:
|
||||
case MESSAGES_THREADED: {
|
||||
case MESSAGES_THREADED:
|
||||
case MESSAGES_THREAD: {
|
||||
List<String> segments = uri.getPathSegments();
|
||||
String accountUuid = segments.get(1);
|
||||
|
||||
@ -238,11 +254,16 @@ public class EmailProvider extends ContentProvider {
|
||||
} else if (match == MESSAGES_THREADED) {
|
||||
cursor = getThreadedMessages(accountUuid, dbProjection, selection,
|
||||
selectionArgs, sortOrder);
|
||||
} else if (match == MESSAGES_THREAD) {
|
||||
String threadId = segments.get(3);
|
||||
cursor = getThread(accountUuid, dbProjection, threadId, sortOrder);
|
||||
} else {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
cursor.setNotificationUri(contentResolver, uri);
|
||||
Uri notificationUri = Uri.withAppendedPath(CONTENT_URI, "account/" + accountUuid +
|
||||
"/messages");
|
||||
cursor.setNotificationUri(contentResolver, notificationUri);
|
||||
|
||||
cursor = new SpecialColumnsCursor(new IdTrickeryCursor(cursor), projection,
|
||||
specialColumns);
|
||||
@ -328,6 +349,7 @@ public class EmailProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
query.append(" FROM messages m " +
|
||||
"JOIN threads t ON (t.message_id = m.id) " +
|
||||
"LEFT JOIN folders f ON (m.folder_id = f.id) " +
|
||||
"WHERE ");
|
||||
query.append(SqlQueryBuilder.addPrefixToSelection(FIXUP_MESSAGES_COLUMNS,
|
||||
@ -374,14 +396,20 @@ public class EmailProvider extends ContentProvider {
|
||||
|
||||
if (MessageColumns.DATE.equals(columnName)) {
|
||||
query.append("MAX(m.date) AS " + MessageColumns.DATE);
|
||||
} else if (MessageColumns.THREAD_COUNT.equals(columnName)) {
|
||||
query.append("COUNT(h.id) AS " + MessageColumns.THREAD_COUNT);
|
||||
} else if (SpecialColumns.THREAD_COUNT.equals(columnName)) {
|
||||
query.append("COUNT(h.id) AS " + SpecialColumns.THREAD_COUNT);
|
||||
} else if (SpecialColumns.FOLDER_NAME.equals(columnName)) {
|
||||
query.append("f." + SpecialColumns.FOLDER_NAME + " AS " +
|
||||
SpecialColumns.FOLDER_NAME);
|
||||
} else if (SpecialColumns.INTEGRATE.equals(columnName)) {
|
||||
query.append("f." + SpecialColumns.INTEGRATE + " AS " +
|
||||
SpecialColumns.INTEGRATE);
|
||||
} else if (ThreadColumns.ROOT.equals(columnName)) {
|
||||
// Always return the thread ID of the root message (even for the root
|
||||
// message itself)
|
||||
query.append("CASE WHEN t2." + ThreadColumns.ROOT + " IS NULL THEN " +
|
||||
"t2." + ThreadColumns.ID + " ELSE t2." + ThreadColumns.ROOT +
|
||||
" END AS " + ThreadColumns.ROOT);
|
||||
} else {
|
||||
query.append("m.");
|
||||
query.append(columnName);
|
||||
@ -391,8 +419,10 @@ public class EmailProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
query.append(
|
||||
" FROM messages h JOIN messages m " +
|
||||
"ON (h.id = m.thread_root OR h.id = m.id) ");
|
||||
" FROM messages h " +
|
||||
"LEFT JOIN threads t1 ON (t1.message_id = h.id) " +
|
||||
"LEFT JOIN threads t2 ON (t1.id = t2.id OR t1.id = t2.root) " +
|
||||
"LEFT JOIN messages m ON (m.id = t2.message_id) ");
|
||||
|
||||
if (Utility.arrayContainsAny(projection, (Object[]) FOLDERS_COLUMNS)) {
|
||||
query.append("LEFT JOIN folders f ON (m.folder_id = f.id) ");
|
||||
@ -400,9 +430,9 @@ public class EmailProvider extends ContentProvider {
|
||||
|
||||
query.append(
|
||||
"WHERE " +
|
||||
"(m.deleted = 0 AND " +
|
||||
"(m.empty IS NULL OR m.empty != 1) AND " +
|
||||
"h.thread_root IS NULL) ");
|
||||
"(t1.root IS NULL AND " +
|
||||
"m.deleted = 0 AND " +
|
||||
"(m.empty IS NULL OR m.empty != 1)) ");
|
||||
|
||||
if (!StringUtils.isNullOrEmpty(selection)) {
|
||||
query.append("AND (");
|
||||
@ -427,6 +457,63 @@ public class EmailProvider extends ContentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
protected Cursor getThread(String accountUuid, final String[] projection, final String threadId,
|
||||
final String sortOrder) {
|
||||
|
||||
Account account = getAccount(accountUuid);
|
||||
LockableDatabase database = getDatabase(account);
|
||||
|
||||
try {
|
||||
return database.execute(false, new DbCallback<Cursor>() {
|
||||
@Override
|
||||
public Cursor doDbWork(SQLiteDatabase db) throws WrappedException,
|
||||
UnavailableStorageException {
|
||||
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("SELECT ");
|
||||
boolean first = true;
|
||||
for (String columnName : projection) {
|
||||
if (!first) {
|
||||
query.append(",");
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (MessageColumns.ID.equals(columnName)) {
|
||||
query.append("m." + MessageColumns.ID + " AS " + MessageColumns.ID);
|
||||
} else {
|
||||
query.append(columnName);
|
||||
}
|
||||
}
|
||||
|
||||
query.append(" FROM " + THREADS_TABLE + " t JOIN " + MESSAGES_TABLE + " m " +
|
||||
"ON (m." + MessageColumns.ID + " = t." + ThreadColumns.MESSAGE_ID +
|
||||
") ");
|
||||
|
||||
if (Utility.arrayContainsAny(projection, (Object[]) FOLDERS_COLUMNS)) {
|
||||
query.append("LEFT JOIN " + FOLDERS_TABLE + " f " +
|
||||
"ON (m." + MessageColumns.FOLDER_ID + " = f." + FolderColumns.ID +
|
||||
") ");
|
||||
}
|
||||
|
||||
query.append("WHERE (t." + ThreadColumns.ID + " = ? OR " +
|
||||
ThreadColumns.ROOT + " = ?) AND " +
|
||||
InternalMessageColumns.DELETED + " = 0 AND (" +
|
||||
InternalMessageColumns.EMPTY + " IS NULL OR " +
|
||||
InternalMessageColumns.EMPTY + " != 1)");
|
||||
|
||||
query.append(" ORDER BY ");
|
||||
query.append(SqlQueryBuilder.addPrefixToSelection(FIXUP_MESSAGES_COLUMNS,
|
||||
"m.", sortOrder));
|
||||
|
||||
return db.rawQuery(query.toString(), new String[] { threadId, threadId });
|
||||
}
|
||||
});
|
||||
} catch (UnavailableStorageException e) {
|
||||
throw new RuntimeException("Storage not available", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Cursor getAccountStats(String accountUuid, String[] columns,
|
||||
final String selection, final String[] selectionArgs) {
|
||||
|
||||
|
@ -76,7 +76,7 @@ public interface SearchSpecification extends Parcelable {
|
||||
MESSAGE_CONTENTS,
|
||||
ATTACHMENT_COUNT,
|
||||
DELETED,
|
||||
THREAD_ROOT,
|
||||
THREAD_ID,
|
||||
ID,
|
||||
INTEGRATE,
|
||||
READ,
|
||||
|
@ -66,6 +66,12 @@ public class SqlQueryBuilder {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case THREAD_ID: {
|
||||
query.append("threads.id = ? OR threads.root = ?");
|
||||
selectionArgs.add(condition.value);
|
||||
selectionArgs.add(condition.value);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
appendCondition(condition, query, selectionArgs);
|
||||
}
|
||||
@ -149,10 +155,6 @@ public class SqlQueryBuilder {
|
||||
columnName = "subject";
|
||||
break;
|
||||
}
|
||||
case THREAD_ROOT: {
|
||||
columnName = "thread_root";
|
||||
break;
|
||||
}
|
||||
case TO: {
|
||||
columnName = "to_list";
|
||||
break;
|
||||
@ -177,6 +179,7 @@ public class SqlQueryBuilder {
|
||||
columnName = "display_class";
|
||||
break;
|
||||
}
|
||||
case THREAD_ID:
|
||||
case FOLDER:
|
||||
case SEARCHABLE: {
|
||||
// Special cases handled in buildWhereClauseInternal()
|
||||
@ -258,7 +261,7 @@ public class SqlQueryBuilder {
|
||||
case FOLDER:
|
||||
case ID:
|
||||
case INTEGRATE:
|
||||
case THREAD_ROOT:
|
||||
case THREAD_ID:
|
||||
case READ:
|
||||
case FLAGGED: {
|
||||
return true;
|
||||
@ -272,7 +275,7 @@ public class SqlQueryBuilder {
|
||||
public static String addPrefixToSelection(String[] columnNames, String prefix, String selection) {
|
||||
String result = selection;
|
||||
for (String columnName : columnNames) {
|
||||
result = result.replaceAll("\\b" + columnName + "\\b", prefix + columnName);
|
||||
result = result.replaceAll("(?<=^|[^\\.])\\b" + columnName + "\\b", prefix + columnName);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
Loading…
Reference in New Issue
Block a user