Merge branch 'readonly-p4-donut' into donut

This commit is contained in:
Andy Stadler 2009-04-29 17:08:26 -07:00 committed by The Android Open Source Project
commit 30eb9bb948
2 changed files with 303 additions and 21 deletions

View File

@ -79,9 +79,11 @@ public class LocalStore extends Store {
* 20 1.5 Added content_id column to attachments table.
* 21 - Added remote_store_data table
* 22 - Added store_flag_1 and store_flag_2 columns to messages table.
* 23 - Added flag_downloaded_full, flag_downloaded_partial, flag_deleted
* columns to message table.
*/
private static final int DB_VERSION = 22;
private static final int DB_VERSION = 23;
private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.X_DESTROYED, Flag.SEEN };
@ -144,7 +146,8 @@ public class LocalStore extends Store {
"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, store_flag_1 INTEGER, " +
"store_flag_2 INTEGER)");
"store_flag_2 INTEGER, flag_downloaded_full INTEGER," +
"flag_downloaded_partial INTEGER, flag_deleted INTEGER)");
mDb.execSQL("DROP TABLE IF EXISTS attachments");
mDb.execSQL("CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER,"
@ -194,6 +197,26 @@ public class LocalStore extends Store {
mDb.execSQL("ALTER TABLE messages ADD COLUMN store_flag_2 INTEGER;");
mDb.setVersion(22);
}
if (oldVersion < 23) {
/**
* Upgrade 22 to 23: add flag_downloaded_full & flag_downloaded_partial
* and flag_deleted columns to message table *and upgrade existing messages*.
*/
mDb.beginTransaction();
try {
mDb.execSQL(
"ALTER TABLE messages ADD COLUMN flag_downloaded_full INTEGER;");
mDb.execSQL(
"ALTER TABLE messages ADD COLUMN flag_downloaded_partial INTEGER;");
mDb.execSQL(
"ALTER TABLE messages ADD COLUMN flag_deleted INTEGER;");
migrateMessageFlags();
mDb.setVersion(23);
mDb.setTransactionSuccessful();
} finally {
mDb.endTransaction();
}
}
}
if (mDb.getVersion() != DB_VERSION) {
@ -229,6 +252,52 @@ public class LocalStore extends Store {
+ "DELETE FROM remote_store_data WHERE old.id = folder_id; "
+ "END;");
}
/**
* When upgrading from 22 to 23, we have to move any flags "X_DOWNLOADED_FULL" or
* "X_DOWNLOADED_PARTIAL" or "DELETED" from the old string-based storage to their own columns.
*
* Note: Caller should open a db transaction around this
*/
private void migrateMessageFlags() {
Cursor cursor = mDb.query("messages",
new String[] { "id", "flags" },
null, null, null, null, null);
try {
int columnId = cursor.getColumnIndexOrThrow("id");
int columnFlags = cursor.getColumnIndexOrThrow("flags");
while (cursor.moveToNext()) {
String oldFlags = cursor.getString(columnFlags);
ContentValues values = new ContentValues();
int newFlagDlFull = 0;
int newFlagDlPartial = 0;
int newFlagDeleted = 0;
if (oldFlags != null) {
if (oldFlags.contains(Flag.X_DOWNLOADED_FULL.toString())) {
newFlagDlFull = 1;
}
if (oldFlags.contains(Flag.X_DOWNLOADED_PARTIAL.toString())) {
newFlagDlPartial = 1;
}
if (oldFlags.contains(Flag.DELETED.toString())) {
newFlagDeleted = 1;
}
}
// Always commit the new flags.
// Note: We don't have to pay the cost of rewriting the old string,
// because the old flag will be ignored, and will eventually be overwritten
// anyway.
values.put("flag_downloaded_full", newFlagDlFull);
values.put("flag_downloaded_partial", newFlagDlPartial);
values.put("flag_deleted", newFlagDeleted);
int rowId = cursor.getInt(columnId);
mDb.update("messages", values, "id=" + rowId, null);
}
} finally {
cursor.close();
}
}
@Override
public Folder getFolder(String name) throws MessagingException {
@ -681,7 +750,8 @@ public class LocalStore extends Store {
private final String POPULATE_MESSAGE_SELECT_COLUMNS =
"subject, sender_list, date, uid, flags, id, to_list, cc_list, " +
"bcc_list, reply_to_list, attachment_count, internal_date, message_id, " +
"store_flag_1, store_flag_2";
"store_flag_1, store_flag_2, flag_downloaded_full, flag_downloaded_partial, " +
"flag_deleted";
/**
* Populate a message from a cursor with the following columns:
@ -701,6 +771,9 @@ public class LocalStore extends Store {
* 12 message id (from Mime headers)
* 13 store flag 1
* 14 store flag 2
* 15 flag "downloaded full"
* 16 flag "downloaded partial"
* 17 flag "deleted"
*/
private void populateMessageFromGetMessageCursor(LocalMessage message, Cursor cursor)
throws MessagingException{
@ -731,6 +804,9 @@ public class LocalStore extends Store {
message.setMessageId(cursor.getString(12));
message.setFlagInternal(Flag.X_STORE_1, (0 != cursor.getInt(13)));
message.setFlagInternal(Flag.X_STORE_2, (0 != cursor.getInt(14)));
message.setFlagInternal(Flag.X_DOWNLOADED_FULL, (0 != cursor.getInt(15)));
message.setFlagInternal(Flag.X_DOWNLOADED_PARTIAL, (0 != cursor.getInt(16)));
message.setFlagInternal(Flag.DELETED, (0 != cursor.getInt(17)));
}
@Override
@ -833,6 +909,12 @@ public class LocalStore extends Store {
sql.append("store_flag_1 = 1 AND ");
} else if (flag == Flag.X_STORE_2) {
sql.append("store_flag_2 = 1 AND ");
} else if (flag == Flag.X_DOWNLOADED_FULL) {
sql.append("flag_downloaded_full = 1 AND ");
} else if (flag == Flag.X_DOWNLOADED_PARTIAL) {
sql.append("flag_downloaded_partial = 1 AND ");
} else if (flag == Flag.DELETED) {
sql.append("flag_deleted = 1 AND ");
} else {
throw new MessagingException("Unsupported flag " + flag);
}
@ -844,6 +926,12 @@ public class LocalStore extends Store {
sql.append("store_flag_1 = 0 AND ");
} else if (flag == Flag.X_STORE_2) {
sql.append("store_flag_2 = 0 AND ");
} else if (flag == Flag.X_DOWNLOADED_FULL) {
sql.append("flag_downloaded_full = 0 AND ");
} else if (flag == Flag.X_DOWNLOADED_PARTIAL) {
sql.append("flag_downloaded_partial = 0 AND ");
} else if (flag == Flag.DELETED) {
sql.append("flag_deleted = 0 AND ");
} else {
throw new MessagingException("Unsupported flag " + flag);
}
@ -968,6 +1056,11 @@ public class LocalStore extends Store {
cv.put("message_id", ((MimeMessage)message).getMessageId());
cv.put("store_flag_1", makeFlagNumeric(message, Flag.X_STORE_1));
cv.put("store_flag_2", makeFlagNumeric(message, Flag.X_STORE_2));
cv.put("flag_downloaded_full",
makeFlagNumeric(message, Flag.X_DOWNLOADED_FULL));
cv.put("flag_downloaded_partial",
makeFlagNumeric(message, Flag.X_DOWNLOADED_PARTIAL));
cv.put("flag_deleted", makeFlagNumeric(message, Flag.DELETED));
long messageId = mDb.insert("messages", "uid", cv);
for (Part attachment : attachments) {
saveAttachment(messageId, attachment, copy);
@ -1021,7 +1114,8 @@ public class LocalStore extends Store {
+ "folder_id = ?, to_list = ?, cc_list = ?, bcc_list = ?, "
+ "html_content = ?, text_content = ?, reply_to_list = ?, "
+ "attachment_count = ?, message_id = ?, store_flag_1 = ?, "
+ "store_flag_2 = ? "
+ "store_flag_2 = ?, flag_downloaded_full = ?, "
+ "flag_downloaded_partial = ?, flag_deleted = ? "
+ "WHERE id = ?",
new Object[] {
message.getUid(),
@ -1045,6 +1139,9 @@ public class LocalStore extends Store {
message.getMessageId(),
makeFlagNumeric(message, Flag.X_STORE_1),
makeFlagNumeric(message, Flag.X_STORE_2),
makeFlagNumeric(message, Flag.X_DOWNLOADED_FULL),
makeFlagNumeric(message, Flag.X_DOWNLOADED_PARTIAL),
makeFlagNumeric(message, Flag.DELETED),
message.mId
});
@ -1318,6 +1415,9 @@ public class LocalStore extends Store {
* Transactionally combine a key/value and a complete message flags flip. Used
* for setting sync bits in messages.
*
* Note: Not all flags are supported here and can only be changed with Message.setFlag().
* For example, Flag.DELETED has side effects (removes attachments).
*
* @param key
* @param value
* @param setFlags
@ -1340,6 +1440,10 @@ public class LocalStore extends Store {
cv.put("store_flag_1", 1);
} else if (flag == Flag.X_STORE_2) {
cv.put("store_flag_2", 1);
} else if (flag == Flag.X_DOWNLOADED_FULL) {
cv.put("flag_downloaded_full", 1);
} else if (flag == Flag.X_DOWNLOADED_PARTIAL) {
cv.put("flag_downloaded_partial", 1);
} else {
throw new MessagingException("Unsupported flag " + flag);
}
@ -1351,6 +1455,10 @@ public class LocalStore extends Store {
cv.put("store_flag_1", 0);
} else if (flag == Flag.X_STORE_2) {
cv.put("store_flag_2", 0);
} else if (flag == Flag.X_DOWNLOADED_FULL) {
cv.put("flag_downloaded_full", 0);
} else if (flag == Flag.X_DOWNLOADED_PARTIAL) {
cv.put("flag_downloaded_partial", 0);
} else {
throw new MessagingException("Unsupported flag " + flag);
}
@ -1460,12 +1568,16 @@ public class LocalStore extends Store {
* Set the flags on the message.
*/
mDb.execSQL("UPDATE messages "
+ "SET flags = ?, store_flag_1 = ?, store_flag_2 = ? "
+ "SET flags = ?, store_flag_1 = ?, store_flag_2 = ?, "
+ "flag_downloaded_full = ?, flag_downloaded_partial = ?, flag_deleted = ? "
+ "WHERE id = ?",
new Object[] {
makeFlagsString(this),
makeFlagNumeric(this, Flag.X_STORE_1),
makeFlagNumeric(this, Flag.X_STORE_2),
makeFlagNumeric(this, Flag.X_DOWNLOADED_FULL),
makeFlagNumeric(this, Flag.X_DOWNLOADED_PARTIAL),
makeFlagNumeric(this, Flag.DELETED),
mId
});
}
@ -1481,7 +1593,10 @@ public class LocalStore extends Store {
StringBuilder sb = null;
boolean nonEmpty = false;
for (Flag flag : Flag.values()) {
if ((flag != Flag.X_STORE_1 && flag != Flag.X_STORE_2) && message.isSet(flag)) {
if (flag != Flag.X_STORE_1 && flag != Flag.X_STORE_2 &&
flag != Flag.X_DOWNLOADED_FULL && flag != Flag.X_DOWNLOADED_PARTIAL &&
flag != Flag.DELETED &&
message.isSet(flag)) {
if (sb == null) {
sb = new StringBuilder();
}

View File

@ -63,7 +63,7 @@ public class LocalStoreUnitTests extends AndroidTestCase {
private static final String MESSAGE_ID = "Test-Message-ID";
private static final String MESSAGE_ID_2 = "Test-Message-ID-Second";
private static final int DATABASE_VERSION = 22;
private static final int DATABASE_VERSION = 23;
/* These values are provided by setUp() */
private String mLocalStoreUri = null;
@ -409,6 +409,69 @@ public class LocalStoreUnitTests extends AndroidTestCase {
assertTrue(retrievedEntry.isSet(Flag.X_STORE_2));
}
/**
* Test that messages are being stored with download & delete state flags properly persisted.
*
* This variant tests appendMessages() and updateMessages() and getMessage()
*/
public void testDownloadAndDeletedFlags() throws MessagingException {
final MimeMessage message = buildTestMessage(RECIPIENT_TO, SENDER, SUBJECT, BODY);
message.setMessageId(MESSAGE_ID);
message.setFlag(Flag.X_STORE_1, true);
message.setFlag(Flag.X_STORE_2, false);
message.setFlag(Flag.X_DOWNLOADED_FULL, true);
message.setFlag(Flag.X_DOWNLOADED_PARTIAL, false);
message.setFlag(Flag.DELETED, false);
mFolder.open(OpenMode.READ_WRITE, null);
mFolder.appendMessages(new Message[]{ message });
String localUid = message.getUid();
// Now try to read it back from the database using getMessage()
MimeMessage retrieved = (MimeMessage) mFolder.getMessage(localUid);
assertEquals(MESSAGE_ID, retrieved.getMessageId());
assertTrue(retrieved.isSet(Flag.X_STORE_1));
assertFalse(retrieved.isSet(Flag.X_STORE_2));
assertTrue(retrieved.isSet(Flag.X_DOWNLOADED_FULL));
assertFalse(retrieved.isSet(Flag.X_DOWNLOADED_PARTIAL));
assertFalse(retrieved.isSet(Flag.DELETED));
// Now try to update it using updateMessages()
retrieved.setFlag(Flag.X_STORE_1, false);
retrieved.setFlag(Flag.X_STORE_2, true);
retrieved.setFlag(Flag.X_DOWNLOADED_FULL, false);
retrieved.setFlag(Flag.X_DOWNLOADED_PARTIAL, true);
mFolder.updateMessage((LocalStore.LocalMessage)retrieved);
// And read back once more to confirm the change (using getMessages() to confirm "just one")
Message[] retrievedArray = mFolder.getMessages(null);
assertEquals(1, retrievedArray.length);
MimeMessage retrievedEntry = (MimeMessage) retrievedArray[0];
assertEquals(MESSAGE_ID, retrievedEntry.getMessageId());
assertFalse(retrievedEntry.isSet(Flag.X_STORE_1));
assertTrue(retrievedEntry.isSet(Flag.X_STORE_2));
assertFalse(retrievedEntry.isSet(Flag.X_DOWNLOADED_FULL));
assertTrue(retrievedEntry.isSet(Flag.X_DOWNLOADED_PARTIAL));
assertFalse(retrievedEntry.isSet(Flag.DELETED));
// Finally test setFlag(Flag.DELETED)
retrievedEntry.setFlag(Flag.DELETED, true);
mFolder.updateMessage((LocalStore.LocalMessage)retrievedEntry);
Message[] retrievedArray2 = mFolder.getMessages(null);
assertEquals(1, retrievedArray2.length);
MimeMessage retrievedEntry2 = (MimeMessage) retrievedArray2[0];
assertEquals(MESSAGE_ID, retrievedEntry2.getMessageId());
assertFalse(retrievedEntry2.isSet(Flag.X_STORE_1));
assertTrue(retrievedEntry2.isSet(Flag.X_STORE_2));
assertFalse(retrievedEntry2.isSet(Flag.X_DOWNLOADED_FULL));
assertTrue(retrievedEntry2.isSet(Flag.X_DOWNLOADED_PARTIAL));
assertTrue(retrievedEntry2.isSet(Flag.DELETED));
}
/**
* Test that store flags are separated into separate columns and not replicated in the
* (should be deprecated) string flags column.
@ -420,6 +483,9 @@ public class LocalStoreUnitTests extends AndroidTestCase {
message.setFlag(Flag.FLAGGED, true);
message.setFlag(Flag.X_STORE_1, true);
message.setFlag(Flag.X_STORE_2, true);
message.setFlag(Flag.X_DOWNLOADED_FULL, true);
message.setFlag(Flag.X_DOWNLOADED_PARTIAL, true);
message.setFlag(Flag.DELETED, true);
mFolder.open(OpenMode.READ_WRITE, null);
mFolder.appendMessages(new Message[]{ message });
@ -435,27 +501,31 @@ public class LocalStoreUnitTests extends AndroidTestCase {
Cursor cursor = null;
try {
cursor = db.rawQuery(
"SELECT flags, store_flag_1, store_flag_2" +
"SELECT flags, store_flag_1, store_flag_2," +
" flag_downloaded_full, flag_downloaded_partial, flag_deleted" +
" FROM messages" +
" WHERE uid = ? AND folder_id = ?",
new String[] {
localUid, Long.toString(folderId)
});
if (!cursor.moveToNext()) {
fail("appended message not found");
}
assertTrue("appended message not found", cursor.moveToNext());
String flagString = cursor.getString(0);
String[] flags = flagString.split(",");
assertEquals(2, flags.length); // 2 = SEEN & FLAGGED
for (String flag : flags) {
assertFalse("storeFlag1 in string", flag.equals(Flag.X_STORE_1.toString()));
assertFalse("storeFlag2 in string", flag.equals(Flag.X_STORE_2.toString()));
assertFalse("flag_downloaded_full in string",
flag.equals(Flag.X_DOWNLOADED_FULL.toString()));
assertFalse("flag_downloaded_partial in string",
flag.equals(Flag.X_DOWNLOADED_PARTIAL.toString()));
assertFalse("flag_deleted in string", flag.equals(Flag.DELETED.toString()));
}
int flag1 = cursor.getInt(1); // store flag 1 is set
assertEquals(1, flag1);
int flag2 = cursor.getInt(2); // store flag 2 is set
assertEquals(1, flag2);
assertEquals(1, cursor.getInt(1)); // store flag 1 is set
assertEquals(1, cursor.getInt(2)); // store flag 2 is set
assertEquals(1, cursor.getInt(3)); // flag_downloaded_full is set
assertEquals(1, cursor.getInt(4)); // flag_downloaded_partial is set
assertEquals(1, cursor.getInt(5)); // flag_deleted is set
}
finally {
if (cursor != null) {
@ -485,7 +555,17 @@ public class LocalStoreUnitTests extends AndroidTestCase {
message4.setFlag(Flag.X_STORE_1, true);
message4.setFlag(Flag.X_STORE_2, true);
Message[] allOriginals = new Message[]{ message1, message2, message3, message4 };
final MimeMessage message5 = buildTestMessage(RECIPIENT_TO, SENDER, SUBJECT, BODY);
message5.setFlag(Flag.X_DOWNLOADED_FULL, true);
final MimeMessage message6 = buildTestMessage(RECIPIENT_TO, SENDER, SUBJECT, BODY);
message6.setFlag(Flag.X_DOWNLOADED_PARTIAL, true);
final MimeMessage message7 = buildTestMessage(RECIPIENT_TO, SENDER, SUBJECT, BODY);
message7.setFlag(Flag.DELETED, true);
Message[] allOriginals = new Message[] {
message1, message2, message3, message4, message5, message6, message7 };
mFolder.open(OpenMode.READ_WRITE, null);
mFolder.appendMessages(allOriginals);
@ -506,13 +586,25 @@ public class LocalStoreUnitTests extends AndroidTestCase {
checkGottenMessages("store_1 set", new Message[]{ message2, message4 }, getSome1);
Message[] getSome2 = mFolder.getMessages(null, new Flag[]{ Flag.X_STORE_1 }, null);
checkGottenMessages("store_1 clear", new Message[]{ message1, message3 }, getSome2);
checkGottenMessages("store_1 clear",
new Message[]{ message1, message3, message5, message6, message7 }, getSome2);
Message[] getSome3 = mFolder.getMessages(new Flag[]{ Flag.X_STORE_2 }, null, null);
checkGottenMessages("store_2 set", new Message[]{ message3, message4 }, getSome3);
Message[] getSome4 = mFolder.getMessages(null, new Flag[]{ Flag.X_STORE_2 }, null);
checkGottenMessages("store_2 clear", new Message[]{ message1, message2 }, getSome4);
checkGottenMessages("store_2 clear",
new Message[]{ message1, message2, message5, message6, message7 }, getSome4);
Message[] getOne1 = mFolder.getMessages(new Flag[]{ Flag.X_DOWNLOADED_FULL }, null, null);
checkGottenMessages("downloaded full", new Message[]{ message5 }, getOne1);
Message[] getOne2 = mFolder.getMessages(new Flag[]{ Flag.X_DOWNLOADED_PARTIAL }, null,
null);
checkGottenMessages("downloaded partial", new Message[]{ message6 }, getOne2);
Message[] getOne3 = mFolder.getMessages(new Flag[]{ Flag.DELETED }, null, null);
checkGottenMessages("deleted", new Message[]{ message7 }, getOne3);
// Multi-flag selections
Message[] getSingle1 = mFolder.getMessages(new Flag[]{ Flag.X_STORE_1, Flag.X_STORE_2 },
@ -521,7 +613,8 @@ public class LocalStoreUnitTests extends AndroidTestCase {
Message[] getSingle2 = mFolder.getMessages(null,
new Flag[]{ Flag.X_STORE_1, Flag.X_STORE_2 }, null);
checkGottenMessages("both clear", new Message[]{ message1 }, getSingle2);
checkGottenMessages("both clear", new Message[]{ message1, message5, message6, message7 },
getSingle2);
}
/**
@ -853,6 +946,76 @@ public class LocalStoreUnitTests extends AndroidTestCase {
checkAllTablesFound(db);
}
/**
* Check upgrade from db version 22 to latest.
* Flags must be migrated to new columns.
*/
public void testDbUpgrade22ToLatest() throws MessagingException, URISyntaxException {
final URI uri = new URI(mLocalStoreUri);
final String dbPath = uri.getPath();
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
// create sample version 22 db tables
createSampleDb(db, 22);
// insert three messages, one for each migration flag
final ContentValues inMessage1 = new ContentValues();
inMessage1.put("message_id", (String) "x"); // message_id text == String
inMessage1.put("flags", Flag.X_DOWNLOADED_FULL.toString());
final ContentValues outMessage1 = new ContentValues(inMessage1);
outMessage1.put("id", db.insert("messages", null, inMessage1));
final ContentValues inMessage2 = new ContentValues();
inMessage2.put("message_id", (String) "y"); // message_id text == String
inMessage2.put("flags", Flag.X_DOWNLOADED_PARTIAL.toString());
final ContentValues outMessage2 = new ContentValues(inMessage2);
outMessage2.put("id", db.insert("messages", null, inMessage2));
final ContentValues inMessage3 = new ContentValues();
inMessage3.put("message_id", (String) "z"); // message_id text == String
inMessage3.put("flags", Flag.DELETED.toString());
final ContentValues outMessage3 = new ContentValues(inMessage3);
outMessage3.put("id", db.insert("messages", null, inMessage3));
db.close();
// upgrade database 22 to latest
LocalStore.newInstance(mLocalStoreUri, getContext(), null);
// database should be upgraded
db = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
assertEquals("database should be upgraded", DATABASE_VERSION, db.getVersion());
// check for all "latest version" tables
checkAllTablesFound(db);
// check message table for migrated flags
String[] columns = new String[] { "id", "message_id", "flags",
"flag_downloaded_full", "flag_downloaded_partial", "flag_deleted" };
Cursor c = db.query("messages", columns, null, null, null, null, null);
for (int msgNum = 0; msgNum <= 2; ++msgNum) {
assertTrue(c.moveToNext());
ContentValues actualMessage = cursorToContentValues(c,
new String[] { "primary", "text", "text", "integer", "integer", "integer" });
String messageId = actualMessage.getAsString("message_id");
int outDlFull = actualMessage.getAsInteger("flag_downloaded_full");
int outDlPartial = actualMessage.getAsInteger("flag_downloaded_partial");
int outDeleted = actualMessage.getAsInteger("flag_deleted");
if ("x".equals(messageId)) {
assertTrue("converted flag_downloaded_full",
outDlFull == 1 && outDlPartial == 0 && outDeleted == 0);
} else if ("y".equals(messageId)) {
assertTrue("converted flag_downloaded_partial",
outDlFull == 0 && outDlPartial == 1 && outDeleted == 0);
} else if ("z".equals(messageId)) {
assertTrue("converted flag_deleted",
outDlFull == 0 && outDlPartial == 0 && outDeleted == 1);
}
}
c.close();
}
/**
* Checks the database to confirm that all tables, with all expected columns are found.
*/
@ -870,7 +1033,8 @@ public class LocalStoreUnitTests extends AndroidTestCase {
new String[]{ "id", "folder_id", "uid", "subject", "date", "flags", "sender_list",
"to_list", "cc_list", "bcc_list", "reply_to_list",
"html_content", "text_content", "attachment_count",
"internal_date", "store_flag_1", "store_flag_2" }
"internal_date", "store_flag_1", "store_flag_2", "flag_downloaded_full",
"flag_downloaded_partial", "flag_deleted" }
));
assertTrue("messages", foundNames.containsAll(expectedNames));
@ -906,6 +1070,9 @@ public class LocalStoreUnitTests extends AndroidTestCase {
"internal_date INTEGER" +
((version >= 19) ? ", message_id TEXT" : "") +
((version >= 22) ? ", store_flag_1 INTEGER, store_flag_2 INTEGER" : "") +
((version >= 23) ?
", flag_downloaded_full INTEGER, flag_downloaded_partial INTEGER" : "") +
((version >= 23) ? ", flag_deleted INTEGER" : "") +
")");
db.execSQL("DROP TABLE IF EXISTS attachments");
db.execSQL("CREATE TABLE attachments (id INTEGER PRIMARY KEY, message_id INTEGER," +