Merge change 21583 into eclair
* changes: Add support for syncing the favorite flag to server (EAS 12.0 and up)
This commit is contained in:
commit
d1fd72a44e
@ -1311,10 +1311,12 @@ public class SyncManager extends Service implements Runnable {
|
||||
INSTANCE.notify();
|
||||
}
|
||||
}
|
||||
if (sConnectivityLock != null) {
|
||||
synchronized (sConnectivityLock) {
|
||||
sConnectivityLock.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public void kick(long mailboxId) {
|
||||
if (INSTANCE == null) return;
|
||||
|
@ -31,6 +31,13 @@ import java.io.InputStream;
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractSyncAdapter {
|
||||
|
||||
public static final int SECONDS = 1000;
|
||||
public static final int MINUTES = SECONDS*60;
|
||||
public static final int HOURS = MINUTES*60;
|
||||
public static final int DAYS = HOURS*24;
|
||||
public static final int WEEKS = DAYS*7;
|
||||
|
||||
public Mailbox mMailbox;
|
||||
public EasSyncService mService;
|
||||
public Context mContext;
|
||||
|
@ -49,6 +49,7 @@ import android.webkit.MimeTypeMap;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@ -61,8 +62,10 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
|
||||
private static final int UPDATES_READ_COLUMN = 0;
|
||||
private static final int UPDATES_MAILBOX_KEY_COLUMN = 1;
|
||||
private static final int UPDATES_SERVER_ID_COLUMN = 2;
|
||||
private static final int UPDATES_FLAG_COLUMN = 3;
|
||||
private static final String[] UPDATES_PROJECTION =
|
||||
{MessageColumns.FLAG_READ, MessageColumns.MAILBOX_KEY, SyncColumns.SERVER_ID};
|
||||
{MessageColumns.FLAG_READ, MessageColumns.MAILBOX_KEY, SyncColumns.SERVER_ID,
|
||||
MessageColumns.FLAG_FAVORITE};
|
||||
|
||||
String[] bindArguments = new String[2];
|
||||
|
||||
@ -524,6 +527,36 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private String formatTwo(int num) {
|
||||
if (num < 10) {
|
||||
return "0" + (char)('0' + num);
|
||||
} else
|
||||
return Integer.toString(num);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create date/time in RFC8601 format. Oddly enough, for calendar date/time, Microsoft uses
|
||||
* a different format that excludes the punctuation (this is why I'm not putting this in a
|
||||
* parent class)
|
||||
*/
|
||||
public String formatDateTime(Calendar calendar) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
//YYYY-MM-DDTHH:MM:SS.MSSZ
|
||||
sb.append(calendar.get(Calendar.YEAR));
|
||||
sb.append('-');
|
||||
sb.append(formatTwo(calendar.get(Calendar.MONTH) + 1));
|
||||
sb.append('-');
|
||||
sb.append(formatTwo(calendar.get(Calendar.DAY_OF_MONTH)));
|
||||
sb.append('T');
|
||||
sb.append(formatTwo(calendar.get(Calendar.HOUR_OF_DAY)));
|
||||
sb.append(':');
|
||||
sb.append(formatTwo(calendar.get(Calendar.MINUTE)));
|
||||
sb.append(':');
|
||||
sb.append(formatTwo(calendar.get(Calendar.SECOND)));
|
||||
sb.append(".000Z");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendLocalChanges(Serializer s, EasSyncService service) throws IOException {
|
||||
ContentResolver cr = mContext.getContentResolver();
|
||||
@ -589,23 +622,70 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean flagChange = false;
|
||||
boolean readChange = false;
|
||||
|
||||
int flag = 0;
|
||||
|
||||
// We can only send flag changes to the server in 12.0 or later
|
||||
if (mService.mProtocolVersionDouble >= 12.0) {
|
||||
flag = currentCursor.getInt(UPDATES_FLAG_COLUMN);
|
||||
if (flag != c.getInt(Message.LIST_FAVORITE_COLUMN)) {
|
||||
flagChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
int read = currentCursor.getInt(UPDATES_READ_COLUMN);
|
||||
if (read == c.getInt(Message.LIST_READ_COLUMN)) {
|
||||
// The read state hasn't really changed, so move on...
|
||||
readChange = true;
|
||||
}
|
||||
|
||||
if (!flagChange && !readChange) {
|
||||
// In this case, we've got nothing to send to the server
|
||||
continue;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
s.start(Tags.SYNC_COMMANDS);
|
||||
first = false;
|
||||
}
|
||||
// Send the change to "read". We'll do "flagged" here eventually as well
|
||||
// TODO Add support for flags here (EAS 12.0 and above)
|
||||
// Or is this not safe??
|
||||
// Send the change to "read" and "favorite" (flagged)
|
||||
s.start(Tags.SYNC_CHANGE)
|
||||
.data(Tags.SYNC_SERVER_ID, c.getString(Message.LIST_SERVER_ID_COLUMN))
|
||||
.start(Tags.SYNC_APPLICATION_DATA)
|
||||
.data(Tags.EMAIL_READ, Integer.toString(read))
|
||||
.end().end(); // SYNC_APPLICATION_DATA, SYNC_CHANGE
|
||||
.start(Tags.SYNC_APPLICATION_DATA);
|
||||
if (readChange) {
|
||||
s.data(Tags.EMAIL_READ, Integer.toString(read));
|
||||
}
|
||||
// "Flag" is a relatively complex concept in EAS 12.0 and above. It is not only
|
||||
// the boolean "favorite" that we think of in Gmail, but it also represents a
|
||||
// follow up action, which can include a subject, start and due dates, and even
|
||||
// recurrences. We don't support any of this as yet, but EAS 12.0 and higher
|
||||
// require that a flag contain a status, a type, and four date fields, two each
|
||||
// for start date and end (due) date.
|
||||
if (flagChange) {
|
||||
if (flag != 0) {
|
||||
// Status 2 = set flag
|
||||
s.start(Tags.EMAIL_FLAG).data(Tags.EMAIL_FLAG_STATUS, "2");
|
||||
// "FollowUp" is the standard type
|
||||
s.data(Tags.EMAIL_FLAG_TYPE, "FollowUp");
|
||||
long now = System.currentTimeMillis();
|
||||
Calendar calendar =
|
||||
GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
calendar.setTimeInMillis(now);
|
||||
// Flags are required to have a start date and end date (duplicated)
|
||||
// First, we'll set the current date/time in GMT as the start time
|
||||
String utc = formatDateTime(calendar);
|
||||
s.data(Tags.TASK_START_DATE, utc).data(Tags.TASK_UTC_START_DATE, utc);
|
||||
// And then we'll use one week from today for completion date
|
||||
calendar.setTimeInMillis(now + 1*WEEKS);
|
||||
utc = formatDateTime(calendar);
|
||||
s.data(Tags.TASK_DUE_DATE, utc).data(Tags.TASK_UTC_DUE_DATE, utc);
|
||||
s.end();
|
||||
} else {
|
||||
s.tag(Tags.EMAIL_FLAG);
|
||||
}
|
||||
}
|
||||
s.end().end(); // SYNC_APPLICATION_DATA, SYNC_CHANGE
|
||||
} finally {
|
||||
currentCursor.close();
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ public class Tags {
|
||||
public static final int MOVE = 0x05;
|
||||
public static final int GIE = 0x06;
|
||||
public static final int FOLDER = 0x07;
|
||||
public static final int TASK = 0x09;
|
||||
public static final int CONTACTS2 = 0x0C;
|
||||
public static final int PING = 0x0D;
|
||||
public static final int GAL = 0x10;
|
||||
@ -277,6 +278,39 @@ public class Tags {
|
||||
public static final int EMAIL_FLAG_TYPE = EMAIL_PAGE + 0x3D;
|
||||
public static final int EMAIL_COMPLETE_TIME = EMAIL_PAGE + 0x3E;
|
||||
|
||||
public static final int TASK_PAGE = TASK << PAGE_SHIFT;
|
||||
public static final int TASK_BODY = TASK_PAGE + 5;
|
||||
public static final int TASK_BODY_SIZE = TASK_PAGE + 6;
|
||||
public static final int TASK_BODY_TRUNCATED = TASK_PAGE + 7;
|
||||
public static final int TASK_CATEGORIES = TASK_PAGE + 8;
|
||||
public static final int TASK_CATEGORY = TASK_PAGE + 9;
|
||||
public static final int TASK_COMPLETE = TASK_PAGE + 0xA;
|
||||
public static final int TASK_DATE_COMPLETED = TASK_PAGE + 0xB;
|
||||
public static final int TASK_DUE_DATE = TASK_PAGE + 0xC;
|
||||
public static final int TASK_UTC_DUE_DATE = TASK_PAGE + 0xD;
|
||||
public static final int TASK_IMPORTANCE = TASK_PAGE + 0xE;
|
||||
public static final int TASK_RECURRENCE = TASK_PAGE + 0xF;
|
||||
public static final int TASK_RECURRENCE_TYPE = TASK_PAGE + 0x10;
|
||||
public static final int TASK_RECURRENCE_START = TASK_PAGE + 0x11;
|
||||
public static final int TASK_RECURRENCE_UNTIL = TASK_PAGE + 0x12;
|
||||
public static final int TASK_RECURRENCE_OCCURRENCES = TASK_PAGE + 0x13;
|
||||
public static final int TASK_RECURRENCE_INTERVAL = TASK_PAGE + 0x14;
|
||||
public static final int TASK_RECURRENCE_DAY_OF_MONTH = TASK_PAGE + 0x15;
|
||||
public static final int TASK_RECURRENCE_DAY_OF_WEEK = TASK_PAGE + 0x16;
|
||||
public static final int TASK_RECURRENCE_WEEK_OF_MONTH = TASK_PAGE + 0x17;
|
||||
public static final int TASK_RECURRENCE_MONTH_OF_YEAR = TASK_PAGE + 0x18;
|
||||
public static final int TASK_RECURRENCE_REGENERATE = TASK_PAGE + 0x19;
|
||||
public static final int TASK_RECURRENCE_DEAD_OCCUR = TASK_PAGE + 0x1A;
|
||||
public static final int TASK_REMINDER_SET = TASK_PAGE + 0x1B;
|
||||
public static final int TASK_REMINDER_TIME = TASK_PAGE + 0x1C;
|
||||
public static final int TASK_SENSITIVITY = TASK_PAGE + 0x1D;
|
||||
public static final int TASK_START_DATE = TASK_PAGE + 0x1E;
|
||||
public static final int TASK_UTC_START_DATE = TASK_PAGE + 0x1F;
|
||||
public static final int TASK_SUBJECT = TASK_PAGE + 0x20;
|
||||
public static final int COMPRESSED_RTF = TASK_PAGE + 0x21;
|
||||
public static final int ORDINAL_DATE = TASK_PAGE + 0x22;
|
||||
public static final int SUBORDINAL_DATE = TASK_PAGE + 0x23;
|
||||
|
||||
public static final int MOVE_PAGE = MOVE << PAGE_SHIFT;
|
||||
public static final int MOVE_MOVE_ITEMS = MOVE_PAGE + 5;
|
||||
public static final int MOVE_MOVE = MOVE_PAGE + 6;
|
||||
@ -410,6 +444,13 @@ public class Tags {
|
||||
},
|
||||
{
|
||||
// 0x09 Tasks
|
||||
"Body", "BodySize", "BodyTruncated", "Categories", "Category", "Complete",
|
||||
"DateCompleted", "DueDate", "UTCDueDate", "Importance", "Recurrence", "RecurrenceType",
|
||||
"RecurrenceStart", "RecurrenceUntil", "RecurrenceOccurrences", "RecurrenceInterval",
|
||||
"RecurrenceDOM", "RecurrenceDOW", "RecurrenceWOM", "RecurrenceMOY",
|
||||
"RecurrenceRegenerate", "RecurrenceDeadOccur", "ReminderSet", "ReminderTime",
|
||||
"Sensitivity", "StartDate", "UTCStartDate", "Subject", "CompressedRTF", "OrdinalDate",
|
||||
"SubordinalDate"
|
||||
},
|
||||
{
|
||||
// 0x0A ResolveRecipients
|
||||
|
@ -26,6 +26,8 @@ import android.test.AndroidTestCase;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class EasEmailSyncAdapterTests extends AndroidTestCase {
|
||||
|
||||
@ -38,22 +40,32 @@ public class EasEmailSyncAdapterTests extends AndroidTestCase {
|
||||
return new ByteArrayInputStream(new byte[] {0, 0, 0, 0, 0});
|
||||
}
|
||||
|
||||
EasSyncService getTestService() {
|
||||
Account account = new Account();
|
||||
account.mId = -1;
|
||||
Mailbox mailbox = new Mailbox();
|
||||
mailbox.mId = -1;
|
||||
EasSyncService service = new EasSyncService();
|
||||
service.mContext = getContext();
|
||||
service.mMailbox = mailbox;
|
||||
service.mAccount = account;
|
||||
return service;
|
||||
}
|
||||
|
||||
EmailSyncAdapter getTestSyncAdapter() {
|
||||
EasSyncService service = getTestService();
|
||||
EmailSyncAdapter adapter = new EmailSyncAdapter(service.mMailbox, service);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check functionality for getting mime type from a file name (using its extension)
|
||||
* The default for all unknown files is application/octet-stream
|
||||
*/
|
||||
public void testGetMimeTypeFromFileName() throws IOException {
|
||||
Mailbox mailbox = new Mailbox();
|
||||
mailbox.mId = -1;
|
||||
Account account = new Account();
|
||||
account.mId = -1;
|
||||
EasSyncService service = new EasSyncService();
|
||||
service.mContext = getContext();
|
||||
service.mMailbox = mailbox;
|
||||
service.mAccount = account;
|
||||
EmailSyncAdapter adapter = new EmailSyncAdapter(mailbox, service);
|
||||
EasEmailSyncParser p;
|
||||
p = adapter.new EasEmailSyncParser(getTestInputStream(), service);
|
||||
EasSyncService service = getTestService();
|
||||
EmailSyncAdapter adapter = new EmailSyncAdapter(service.mMailbox, service);
|
||||
EasEmailSyncParser p = adapter.new EasEmailSyncParser(getTestInputStream(), service);
|
||||
// Test a few known types
|
||||
String mimeType = p.getMimeTypeFromFileName("foo.jpg");
|
||||
assertEquals("image/jpeg", mimeType);
|
||||
@ -72,4 +84,17 @@ public class EasEmailSyncAdapterTests extends AndroidTestCase {
|
||||
mimeType = p.getMimeTypeFromFileName("");
|
||||
assertEquals("application/octet-stream", mimeType);
|
||||
}
|
||||
|
||||
public void testFormatDateTime() throws IOException {
|
||||
EmailSyncAdapter adapter = getTestSyncAdapter();
|
||||
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
// Calendar is odd, months are zero based, so the first 11 below is December...
|
||||
calendar.set(2008, 11, 11, 18, 19, 20);
|
||||
String date = adapter.formatDateTime(calendar);
|
||||
assertEquals("2008-12-11T18:19:20.000Z", date);
|
||||
calendar.clear();
|
||||
calendar.set(2012, 0, 2, 23, 0, 1);
|
||||
date = adapter.formatDateTime(calendar);
|
||||
assertEquals("2012-01-02T23:00:01.000Z", date);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user