diff options
Diffstat (limited to 'src/com/android/exchange')
-rw-r--r-- | src/com/android/exchange/Eas.java | 22 | ||||
-rw-r--r-- | src/com/android/exchange/EasSyncService.java | 83 | ||||
-rw-r--r-- | src/com/android/exchange/EmailContent.java | 135 | ||||
-rw-r--r-- | src/com/android/exchange/SyncManager.java | 42 | ||||
-rw-r--r-- | src/com/android/exchange/adapter/EasEmailSyncAdapter.java | 110 | ||||
-rw-r--r-- | src/com/android/exchange/adapter/EasFolderSyncParser.java | 12 | ||||
-rw-r--r-- | src/com/android/exchange/adapter/EasOutboxService.java | 9 | ||||
-rw-r--r-- | src/com/android/exchange/adapter/EasTags.java | 7 | ||||
-rw-r--r-- | src/com/android/exchange/utility/Rfc822Formatter.java | 15 |
9 files changed, 275 insertions, 160 deletions
diff --git a/src/com/android/exchange/Eas.java b/src/com/android/exchange/Eas.java index 03828911..dc23195e 100644 --- a/src/com/android/exchange/Eas.java +++ b/src/com/android/exchange/Eas.java @@ -44,18 +44,18 @@ public class Eas { // 6 3 months ago No Yes // 7 6 months ago No Yes - static final String FILTER_ALL = "0"; - static final String FILTER_1_DAY = "1"; - static final String FILTER_3_DAYS = "2"; - static final String FILTER_1_WEEK = "3"; - static final String FILTER_2_WEEKS = "4"; - static final String FILTER_1_MONTH = "5"; - static final String FILTER_3_MONTHS = "6"; - static final String FILTER_6_MONTHS = "7"; - static final String BODY_PREFERENCE_TEXT = "1"; - static final String BODY_PREFERENCE_HTML = "2"; + public static final String FILTER_ALL = "0"; + public static final String FILTER_1_DAY = "1"; + public static final String FILTER_3_DAYS = "2"; + public static final String FILTER_1_WEEK = "3"; + public static final String FILTER_2_WEEKS = "4"; + public static final String FILTER_1_MONTH = "5"; + public static final String FILTER_3_MONTHS = "6"; + public static final String FILTER_6_MONTHS = "7"; + public static final String BODY_PREFERENCE_TEXT = "1"; + public static final String BODY_PREFERENCE_HTML = "2"; - static final String DEFAULT_BODY_TRUNCATION_SIZE = "50000"; + public static final String DEFAULT_BODY_TRUNCATION_SIZE = "50000"; public static final int FOLDER_STATUS_OK = 1; public static final int FOLDER_STATUS_INVALID_KEY = 9; diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java index 0be715ee..0dd5b566 100644 --- a/src/com/android/exchange/EasSyncService.java +++ b/src/com/android/exchange/EasSyncService.java @@ -17,34 +17,6 @@ package com.android.exchange; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.ProtocolException; -import java.net.URI; -import java.net.URL; -import java.net.URLEncoder; - -import java.util.ArrayList; - -import javax.net.ssl.HttpsURLConnection; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.ssl.AllowAllHostnameVerifier; -import org.apache.http.impl.client.DefaultHttpClient; - import com.android.email.mail.AuthenticationFailedException; import com.android.email.mail.MessagingException; import com.android.exchange.EmailContent.Account; @@ -62,6 +34,12 @@ import com.android.exchange.adapter.EasSyncAdapter; import com.android.exchange.adapter.EasParser.EasParserException; import com.android.exchange.utility.Base64; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.AllowAllHostnameVerifier; +import org.apache.http.impl.client.DefaultHttpClient; + import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; @@ -69,6 +47,27 @@ import android.database.Cursor; import android.os.RemoteException; import android.util.Log; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.URI; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; + +import javax.net.ssl.HttpsURLConnection; + public class EasSyncService extends InteractiveSyncService { private static final String WINDOW_SIZE = "10"; @@ -85,6 +84,7 @@ public class EasSyncService extends InteractiveSyncService { // Reasonable default String mProtocolVersion = "2.5"; + public Double mProtocolVersionDouble; static String mDeviceId = null; static String mDeviceType = "Android"; EasSyncAdapter mTarget; @@ -408,9 +408,10 @@ public class EasSyncService extends InteractiveSyncService { if (mVersions.contains("12.0")) { mProtocolVersion = "12.0"; } - // TODO We only do 2.5 at the moment; add 'else' above when fixed - mProtocolVersion = "2.5"; + mProtocolVersionDouble = Double.parseDouble(mProtocolVersion); + mAccount.mProtocolVersion = mProtocolVersion; userLog(mVersions); + userLog("Using version " + mProtocolVersion); } else { throw new IOException(); } @@ -707,7 +708,6 @@ public class EasSyncService extends InteractiveSyncService { s.data("WindowSize", WINDOW_SIZE); boolean options = false; if (!className.equals("Contacts")) { - options = true; // Set the lookback appropriately (EAS calls this a "filter") String filter = Eas.FILTER_1_WEEK; switch (mAccount.mSyncLookback) { @@ -736,19 +736,21 @@ public class EasSyncService extends InteractiveSyncService { break; } } - s.start("Options") - .data("FilterType", filter); + s.start("Options").data("FilterType", filter); + if (mProtocolVersionDouble < 12.0) { + s.data("Truncation", "7"); + } + options = true; } - if (mProtocolVersion.equals("12.0")) { + if (mProtocolVersionDouble >= 12.0) { if (!options) { options = true; s.start("Options"); - s.start("BodyPreference") - // Plain text to start - .data("BodyPreferenceType", Eas.BODY_PREFERENCE_TEXT) - .data("BodyPreferenceTruncationSize", Eas.DEFAULT_BODY_TRUNCATION_SIZE) - .end("BodyPreference"); } + s.start("BodyPreference") + .data("BodyPreferenceType", Eas.BODY_PREFERENCE_HTML) + .data("BodyPreferenceTruncationSize", Eas.DEFAULT_BODY_TRUNCATION_SIZE) + .end("BodyPreference"); } if (options) { s.end("Options"); @@ -799,6 +801,9 @@ public class EasSyncService extends InteractiveSyncService { runMain(); } else { EasSyncAdapter target; + mAccount = Account.restoreAccountWithId(mContext, mAccount.mId); + mProtocolVersion = mAccount.mProtocolVersion; + mProtocolVersionDouble = Double.parseDouble(mProtocolVersion); if (mMailbox.mType == Mailbox.TYPE_CONTACTS) target = new EasContactsSyncAdapter(mMailbox); else { diff --git a/src/com/android/exchange/EmailContent.java b/src/com/android/exchange/EmailContent.java index 2065e1b3..0adae237 100644 --- a/src/com/android/exchange/EmailContent.java +++ b/src/com/android/exchange/EmailContent.java @@ -15,12 +15,6 @@ */ package com.android.exchange; -/** -* This is a local copy of com.android.email.EmailProvider -* -* Last copied from com.android.email.EmailProvider on 7/16/09 -*/ - import com.android.email.R; import com.android.email.provider.EmailProvider; @@ -44,6 +38,12 @@ import java.util.ArrayList; import java.util.UUID; /** + * This is a local copy of com.android.email.EmailProvider + * + * Last copied from com.android.email.EmailProvider on 7/18/09 + */ + +/** * EmailContent is the superclass of the various classes of content stored by EmailProvider. * * It is intended to include 1) column definitions for use with the Provider, and 2) convenience @@ -244,30 +244,41 @@ public abstract class EmailContent { values.put(BodyColumns.TEXT_CONTENT, mTextContent); return values; - } + } - public static Body restoreBodyWithId(Context context, long id) { - Uri u = ContentUris.withAppendedId(Body.CONTENT_URI, id); - Cursor c = context.getContentResolver().query(u, Body.CONTENT_PROJECTION, - null, null, null); + private static Body restoreBodyWithCursor(Cursor cursor) { + try { + if (cursor.moveToFirst()) { + return getContent(cursor, Body.class); + } else { + return null; + } + } finally { + cursor.close(); + } + } - try { - if (c.moveToFirst()) { - return getContent(c, Body.class); - } else { - return null; - } - } finally { - c.close(); - } - } + public static Body restoreBodyWithId(Context context, long id) { + Uri u = ContentUris.withAppendedId(Body.CONTENT_URI, id); + Cursor c = context.getContentResolver().query(u, Body.CONTENT_PROJECTION, + null, null, null); + return restoreBodyWithCursor(c); + } + + public static Body restoreBodyWithMessageId(Context context, long messageId) { + Cursor c = context.getContentResolver().query(Body.CONTENT_URI, + Body.CONTENT_PROJECTION, Body.MESSAGE_KEY + "=?", + new String[] {Long.toString(messageId)}, null); + return restoreBodyWithCursor(c); + } - private static String restoreTextWithId(Context context, long id, String[] projection) { - Uri u = ContentUris.withAppendedId(Body.CONTENT_URI, id); - Cursor c = context.getContentResolver().query(u, projection, null, null, null); + private static String restoreTextWithMessageId(Context context, long messageId, + String[] projection) { + Cursor c = context.getContentResolver().query(Body.CONTENT_URI, projection, + Body.MESSAGE_KEY + "=?", new String[] {Long.toString(messageId)}, null); try { if (c.moveToFirst()) { - return c.getString(COMMON_TEXT_COLUMN); + return c.getString(COMMON_TEXT_COLUMN); } else { return null; } @@ -276,12 +287,12 @@ public abstract class EmailContent { } } - public static String restoreBodyTextWithId(Context context, long id) { - return restoreTextWithId(context, id, Body.TEXT_PROJECTION); + public static String restoreBodyTextWithMessageId(Context context, long messageId) { + return restoreTextWithMessageId(context, messageId, Body.TEXT_PROJECTION); } - public static String restoreBodyHtmlWithId(Context context, long id) { - return restoreTextWithId(context, id, Body.HTML_PROJECTION); + public static String restoreBodyHtmlWithMessageId(Context context, long messageId) { + return restoreTextWithMessageId(context, messageId, Body.HTML_PROJECTION); } @Override @@ -345,7 +356,7 @@ public abstract class EmailContent { // Foreign key to a referenced Message (e.g. for a reply/forward) public static final String REFERENCE_KEY = "referenceKey"; - // Address lists, of the form <address> [, <address> ...] + // Address lists, packed with Address.pack() public static final String SENDER_LIST = "senderList"; public static final String FROM_LIST = "fromList"; public static final String TO_LIST = "toList"; @@ -495,9 +506,6 @@ public abstract class EmailContent { public static final int LOADED = 1; public static final int PARTIALLY_LOADED = 2; - /** - * no public constructor since this is a utility class - */ public Message() { mBaseUri = CONTENT_URI; } @@ -565,7 +573,8 @@ public abstract class EmailContent { @Override @SuppressWarnings("unchecked") public EmailContent.Message restore(Cursor c) { - mBaseUri = EmailContent.Message.CONTENT_URI; + mBaseUri = CONTENT_URI; + mId = c.getLong(CONTENT_ID_COLUMN); mDisplayName = c.getString(CONTENT_DISPLAY_NAME_COLUMN); mTimeStamp = c.getLong(CONTENT_TIMESTAMP_COLUMN); mSubject = c.getString(CONTENT_SUBJECT_COLUMN); @@ -614,7 +623,8 @@ public abstract class EmailContent { // This logic is in place so I can (a) short circuit the expensive stuff when // possible, and (b) override (and throw) if anyone tries to call save() or update() // directly for Message, which are unsupported. - if (mText == null && mHtml == null) { + if (mText == null && mHtml == null && + (mAttachments == null || mAttachments.isEmpty())) { if (doSave) { return super.save(context); } else { @@ -635,6 +645,17 @@ public abstract class EmailContent { if (doSave) { Uri u = results[0].uri; mId = Long.parseLong(u.getPathSegments().get(1)); + if (mAttachments != null) { + int resultOffset = 2; + for (Attachment a : mAttachments) { + // Save the id of the attachment record + u = results[resultOffset++].uri; + if (u != null) { + a.mId = Long.parseLong(u.getPathSegments().get(1)); + } + a.mMessageKey = mId; + } + } return u; } else { return null; @@ -648,7 +669,7 @@ public abstract class EmailContent { } public void addSaveOps(ArrayList<ContentProviderOperation> ops) { - // First, save the message + // First, save the message ContentProviderOperation.Builder b = getSaveOrUpdateBuilder(true, mBaseUri, -1); ops.add(b.withValues(toContentValues()).build()); @@ -671,12 +692,12 @@ public abstract class EmailContent { if (mAttachments != null) { for (Attachment att: mAttachments) { ops.add(getSaveOrUpdateBuilder(true, Attachment.CONTENT_URI, -1) - .withValues(att.toContentValues()) - .withValueBackReference(Attachment.MESSAGE_KEY, messageBackValue) - .build()); + .withValues(att.toContentValues()) + .withValueBackReference(Attachment.MESSAGE_KEY, messageBackValue) + .build()); } } - } + } // Text and Html information are stored as <location>;<encoding>;<charset>;<length> // charset: U = us-ascii; 8 = utf-8; I = iso-8559-1; others literally (e.g. KOI8-R) @@ -749,6 +770,8 @@ public abstract class EmailContent { public static final String SENDER_NAME = "senderName"; // Ringtone public static final String RINGTONE_URI = "ringtoneUri"; + // Protocol version (arbitrary string, used by EAS currently) + public static final String PROTOCOL_VERSION = "protocolVersion"; } public static final class Account extends EmailContent implements AccountColumns, Parcelable { @@ -783,6 +806,7 @@ public abstract class EmailContent { public String mCompatibilityUuid; public String mSenderName; public String mRingtoneUri; + public String mProtocolVersion; // Convenience for creating an account public transient HostAuth mHostAuthRecv; @@ -801,6 +825,7 @@ public abstract class EmailContent { public static final int CONTENT_COMPATIBILITY_UUID_COLUMN = 10; public static final int CONTENT_SENDER_NAME_COLUMN = 11; public static final int CONTENT_RINGTONE_URI_COLUMN = 12; + public static final int CONTENT_PROTOCOL_VERSION_COLUMN = 13; public static final String[] CONTENT_PROJECTION = new String[] { RECORD_ID, AccountColumns.DISPLAY_NAME, @@ -808,7 +833,7 @@ public abstract class EmailContent { AccountColumns.SYNC_FREQUENCY, AccountColumns.HOST_AUTH_KEY_RECV, AccountColumns.HOST_AUTH_KEY_SEND, AccountColumns.FLAGS, AccountColumns.IS_DEFAULT, AccountColumns.COMPATIBILITY_UUID, AccountColumns.SENDER_NAME, - AccountColumns.RINGTONE_URI, + AccountColumns.RINGTONE_URI, AccountColumns.PROTOCOL_VERSION }; /** @@ -868,7 +893,8 @@ public abstract class EmailContent { @Override @SuppressWarnings("unchecked") public EmailContent.Account restore(Cursor cursor) { - mBaseUri = EmailContent.Account.CONTENT_URI; + mId = cursor.getLong(CONTENT_ID_COLUMN); + mBaseUri = CONTENT_URI; mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN); mEmailAddress = cursor.getString(CONTENT_EMAIL_ADDRESS_COLUMN); mSyncKey = cursor.getString(CONTENT_SYNC_KEY_COLUMN); @@ -881,6 +907,7 @@ public abstract class EmailContent { mCompatibilityUuid = cursor.getString(CONTENT_COMPATIBILITY_UUID_COLUMN); mSenderName = cursor.getString(CONTENT_SENDER_NAME_COLUMN); mRingtoneUri = cursor.getString(CONTENT_RINGTONE_URI_COLUMN); + mProtocolVersion = cursor.getString(CONTENT_PROTOCOL_VERSION_COLUMN); return this; } @@ -1288,6 +1315,7 @@ public abstract class EmailContent { values.put(AccountColumns.COMPATIBILITY_UUID, mCompatibilityUuid); values.put(AccountColumns.SENDER_NAME, mSenderName); values.put(AccountColumns.RINGTONE_URI, mRingtoneUri); + values.put(AccountColumns.PROTOCOL_VERSION, mProtocolVersion); return values; } @@ -1449,7 +1477,9 @@ public abstract class EmailContent { public static final class Attachment extends EmailContent implements AttachmentColumns { public static final String TABLE_NAME = "Attachment"; public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment"); - + // This must be used with an appended id: ContentUris.withAppendedId(MESSAGE_ID_URI, id) + public static final Uri MESSAGE_ID_URI = Uri.parse( + EmailContent.CONTENT_URI + "/attachment/message"); public String mFileName; public String mMimeType; @@ -1511,6 +1541,7 @@ public abstract class EmailContent { * @return a new File object, or null if one could not be created */ public static File createUniqueFile(String filename) { + // TODO Handle internal storage, as required if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { File directory = Environment.getExternalStorageDirectory(); File file = new File(directory, filename); @@ -1539,7 +1570,8 @@ public abstract class EmailContent { @Override @SuppressWarnings("unchecked") public EmailContent.Attachment restore(Cursor cursor) { - mBaseUri = EmailContent.Attachment.CONTENT_URI; + mBaseUri = CONTENT_URI; + mId = cursor.getLong(CONTENT_ID_COLUMN); mFileName= cursor.getString(CONTENT_FILENAME_COLUMN); mMimeType = cursor.getString(CONTENT_MIME_TYPE_COLUMN); mSize = cursor.getLong(CONTENT_SIZE_COLUMN); @@ -1680,6 +1712,7 @@ public abstract class EmailContent { MailboxColumns.SYNC_FREQUENCY, MailboxColumns.SYNC_TIME,MailboxColumns.UNREAD_COUNT, MailboxColumns.FLAG_VISIBLE, MailboxColumns.FLAGS, MailboxColumns.VISIBLE_LIMIT }; + public static final long NO_MAILBOX = -1; private static final String WHERE_TYPE_AND_ACCOUNT_KEY = MailboxColumns.TYPE + "=? and " + MailboxColumns.ACCOUNT_KEY + "=?"; @@ -1687,14 +1720,10 @@ public abstract class EmailContent { private static final int ID_PROJECTION_ID = 0; private static final String[] ID_PROJECTION = new String[] { ID }; - /** - * no public constructor since this is a utility class - */ public Mailbox() { mBaseUri = CONTENT_URI; } - // Types of mailboxes. The list is ordered to match a typical UI presentation, e.g. // placing the inbox at the top. // The "main" mailbox for the account, almost always referred to as "Inbox" @@ -1751,7 +1780,8 @@ public abstract class EmailContent { @Override @SuppressWarnings("unchecked") public EmailContent.Mailbox restore(Cursor cursor) { - mBaseUri = EmailContent.Mailbox.CONTENT_URI; + mBaseUri = CONTENT_URI; + mId = cursor.getLong(CONTENT_ID_COLUMN); mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN); mServerId = cursor.getString(CONTENT_SERVER_ID_COLUMN); mParentServerId = cursor.getString(CONTENT_PARENT_SERVER_ID_COLUMN); @@ -1797,7 +1827,7 @@ public abstract class EmailContent { * @return the id of the mailbox, or -1 if not found */ public static long findMailboxOfType(Context context, long accountId, int type) { - long mailboxId = -1; + long mailboxId = NO_MAILBOX; String[] bindArguments = new String[] {Long.toString(type), Long.toString(accountId)}; Cursor c = context.getContentResolver().query(Mailbox.CONTENT_URI, ID_PROJECTION, WHERE_TYPE_AND_ACCOUNT_KEY, bindArguments, null); @@ -1901,7 +1931,8 @@ public abstract class EmailContent { @Override @SuppressWarnings("unchecked") public EmailContent.HostAuth restore(Cursor cursor) { - mBaseUri = EmailContent.HostAuth.CONTENT_URI; + mBaseUri = CONTENT_URI; + mId = cursor.getLong(CONTENT_ID_COLUMN); mProtocol = cursor.getString(CONTENT_PROTOCOL_COLUMN); mAddress = cursor.getString(CONTENT_ADDRESS_COLUMN); mPort = cursor.getInt(CONTENT_PORT_COLUMN); @@ -2097,4 +2128,4 @@ public abstract class EmailContent { return getStoreUri(); } } -} +}
\ No newline at end of file diff --git a/src/com/android/exchange/SyncManager.java b/src/com/android/exchange/SyncManager.java index 9ab0a7b9..e0ef0102 100644 --- a/src/com/android/exchange/SyncManager.java +++ b/src/com/android/exchange/SyncManager.java @@ -17,18 +17,9 @@ package com.android.exchange; -import android.app.Service; -import android.content.Intent; -import android.os.IBinder; -import android.util.Log; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - import com.android.email.mail.MessagingException; -import com.android.exchange.EmailContent.Attachment; import com.android.exchange.EmailContent.Account; +import com.android.exchange.EmailContent.Attachment; import com.android.exchange.EmailContent.HostAuth; import com.android.exchange.EmailContent.Mailbox; import com.android.exchange.EmailContent.Message; @@ -38,22 +29,31 @@ import com.android.exchange.adapter.EasOutboxService; import android.app.AlarmManager; import android.app.PendingIntent; +import android.app.Service; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.database.ContentObserver; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.net.NetworkInfo.State; import android.os.Bundle; +import android.os.Debug; import android.os.Handler; +import android.os.IBinder; import android.os.PowerManager; import android.os.RemoteException; import android.os.PowerManager.WakeLock; -import android.database.ContentObserver; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; /** * The SyncManager handles all aspects of starting, maintaining, and stopping the various sync @@ -86,6 +86,7 @@ public class SyncManager extends Service implements Runnable { AccountObserver mAccountObserver; MailboxObserver mMailboxObserver; SyncedMessageObserver mSyncedMessageObserver; + MessageObserver mMessageObserver; String mNextWaitReason; static private HashMap<Long, Boolean> mWakeLocks = new HashMap<Long, Boolean>(); @@ -295,13 +296,24 @@ public class SyncManager extends Service implements Runnable { } } + class MessageObserver extends ContentObserver { + + public MessageObserver(Handler handler) { + super(handler); + } + + public void onChange(boolean selfChange) { + INSTANCE.log("MessageObserver"); + // A rather blunt instrument here. But we don't have information about the URI that + // triggered this, though it must have been an insert + kick(); + } + } + public class SyncStatus { static public final int NOT_RUNNING = 0; - static public final int DIED = 1; - static public final int SYNC = 2; - static public final int IDLE = 3; } @@ -349,6 +361,7 @@ public class SyncManager extends Service implements Runnable { mAccountObserver = new AccountObserver(mHandler); mMailboxObserver = new MailboxObserver(mHandler); mSyncedMessageObserver = new SyncedMessageObserver(mHandler); + mMessageObserver = new MessageObserver(mHandler); // Start our thread... if (mServiceThread == null || !mServiceThread.isAlive()) { @@ -578,6 +591,7 @@ public class SyncManager extends Service implements Runnable { resolver.registerContentObserver(Account.CONTENT_URI, false, mAccountObserver); resolver.registerContentObserver(Mailbox.CONTENT_URI, false, mMailboxObserver); resolver.registerContentObserver(Message.SYNCED_CONTENT_URI, true, mSyncedMessageObserver); + resolver.registerContentObserver(Message.CONTENT_URI, false, mMessageObserver); ConnectivityReceiver cr = new ConnectivityReceiver(); registerReceiver(cr, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); diff --git a/src/com/android/exchange/adapter/EasEmailSyncAdapter.java b/src/com/android/exchange/adapter/EasEmailSyncAdapter.java index 67ac52bb..4b25c112 100644 --- a/src/com/android/exchange/adapter/EasEmailSyncAdapter.java +++ b/src/com/android/exchange/adapter/EasEmailSyncAdapter.java @@ -17,12 +17,14 @@ package com.android.exchange.adapter; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.GregorianCalendar; -import java.util.TimeZone; +import com.android.email.provider.EmailProvider; +import com.android.exchange.Eas; +import com.android.exchange.EasSyncService; +import com.android.exchange.EmailContent.Attachment; +import com.android.exchange.EmailContent.Mailbox; +import com.android.exchange.EmailContent.Message; +import com.android.exchange.EmailContent.MessageColumns; +import com.android.exchange.EmailContent.SyncColumns; import android.content.ContentProviderOperation; import android.content.ContentResolver; @@ -32,14 +34,14 @@ import android.content.Context; import android.content.OperationApplicationException; import android.database.Cursor; import android.os.RemoteException; +import android.webkit.MimeTypeMap; -import com.android.email.provider.EmailProvider; -import com.android.exchange.EasSyncService; -import com.android.exchange.EmailContent.Attachment; -import com.android.exchange.EmailContent.Mailbox; -import com.android.exchange.EmailContent.Message; -import com.android.exchange.EmailContent.MessageColumns; -import com.android.exchange.EmailContent.SyncColumns; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.GregorianCalendar; +import java.util.TimeZone; /** * Sync adapter for EAS email @@ -47,6 +49,8 @@ import com.android.exchange.EmailContent.SyncColumns; */ public class EasEmailSyncAdapter extends EasSyncAdapter { + private static boolean DEBUG_LOGGING = false; // DON'T CHECK THIS IN SET TO TRUE + 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; @@ -56,7 +60,6 @@ public class EasEmailSyncAdapter extends EasSyncAdapter { String[] bindArguments = new String[2]; ArrayList<Long> mDeletedIdList = new ArrayList<Long>(); - ArrayList<Long> mUpdatedIdList = new ArrayList<Long>(); public EasEmailSyncAdapter(Mailbox mailbox) { @@ -68,8 +71,8 @@ public class EasEmailSyncAdapter extends EasSyncAdapter { EasEmailSyncParser p = new EasEmailSyncParser(is, service); return p.parse(); } - - class EasEmailSyncParser extends EasContentParser { + + public class EasEmailSyncParser extends EasContentParser { private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY = SyncColumns.SERVER_ID + "=? and " + MessageColumns.MAILBOX_KEY + "=?"; @@ -79,7 +82,9 @@ public class EasEmailSyncAdapter extends EasSyncAdapter { public EasEmailSyncParser(InputStream in, EasSyncService service) throws IOException { super(in, service); mMailboxIdAsString = Long.toString(mMailbox.mId); - //setDebug(true); // DON'T CHECK IN WITH THIS + if (DEBUG_LOGGING) { + setDebug(true); + } } public void wipe() { @@ -144,6 +149,9 @@ public class EasEmailSyncAdapter extends EasSyncAdapter { case EasTags.EMAIL_READ: msg.mFlagRead = getValueInt() == 1; break; + case EasTags.BASE_BODY: + bodyParser(msg); + break; case EasTags.EMAIL_BODY: msg.mTextInfo = "X;X;8;" + size; // location;encoding;charset;size msg.mText = getValue(); @@ -165,7 +173,7 @@ public class EasEmailSyncAdapter extends EasSyncAdapter { } - public void addParser(ArrayList<Message> emails) throws IOException { + private void addParser(ArrayList<Message> emails) throws IOException { Message msg = new Message(); msg.mAccountKey = mAccount.mId; msg.mMailboxKey = mMailbox.mId; @@ -189,7 +197,33 @@ public class EasEmailSyncAdapter extends EasSyncAdapter { emails.add(msg); } - public void attachmentParser(ArrayList<Attachment> atts, Message msg) throws IOException { + private void bodyParser(Message msg) throws IOException { + String bodyType = Eas.BODY_PREFERENCE_TEXT; + String body = ""; + while (nextTag(EasTags.EMAIL_BODY) != END) { + switch (tag) { + case EasTags.BASE_TYPE: + bodyType = getValue(); + break; + case EasTags.BASE_DATA: + body = getValue(); + break; + default: + skipTag(); + } + } + // We always ask for TEXT or HTML; there's no third option + String info = "X;X;8;" + body.length(); + if (bodyType.equals(Eas.BODY_PREFERENCE_HTML)) { + msg.mHtmlInfo = info; + msg.mHtml = body; + } else { + msg.mTextInfo = info; + msg.mText = body; + } + } + + private void attachmentParser(ArrayList<Attachment> atts, Message msg) throws IOException { String fileName = null; String length = null; String location = null; @@ -216,12 +250,41 @@ public class EasEmailSyncAdapter extends EasSyncAdapter { att.mSize = Long.parseLong(length); att.mFileName = fileName; att.mLocation = location; + att.mMimeType = getMimeTypeFromFileName(fileName); atts.add(att); msg.mFlagAttachment = true; } } - public void attachmentsParser(ArrayList<Attachment> atts, Message msg) throws IOException { + /** + * Try to determine a mime type from a file name, defaulting to application/x, where x + * is either the extension or (if none) octet-stream + * At the moment, this is somewhat lame, since many file types aren't recognized + * @param fileName the file name to ponder + * @return + */ + // Note: The MimeTypeMap method currently uses a very limited set of mime types + // A bug has been filed against this issue. + public String getMimeTypeFromFileName(String fileName) { + String mimeType; + int lastDot = fileName.lastIndexOf('.'); + String extension = null; + if (lastDot > 0 && lastDot < fileName.length() - 1) { + extension = fileName.substring(lastDot + 1); + } + if (extension == null) { + // A reasonable default for now. + mimeType = "application/octet-stream"; + } else { + mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + if (mimeType == null) { + mimeType = "application/" + extension; + } + } + return mimeType; + } + + private void attachmentsParser(ArrayList<Attachment> atts, Message msg) throws IOException { while (nextTag(EasTags.EMAIL_ATTACHMENTS) != END) { switch (tag) { case EasTags.EMAIL_ATTACHMENT: @@ -240,7 +303,7 @@ public class EasEmailSyncAdapter extends EasSyncAdapter { WHERE_SERVER_ID_AND_MAILBOX_KEY, bindArguments, null); } - public void deleteParser(ArrayList<Long> deletes) throws IOException { + private void deleteParser(ArrayList<Long> deletes) throws IOException { while (nextTag(EasTags.SYNC_DELETE) != END) { switch (tag) { case EasTags.SYNC_SERVER_ID: @@ -272,7 +335,7 @@ public class EasEmailSyncAdapter extends EasSyncAdapter { } } - public void changeParser(ArrayList<ServerChange> changes) throws IOException { + private void changeParser(ArrayList<ServerChange> changes) throws IOException { String serverId = null; boolean oldRead = false; boolean read = true; @@ -306,6 +369,9 @@ public class EasEmailSyncAdapter extends EasSyncAdapter { } } + /* (non-Javadoc) + * @see com.android.exchange.adapter.EasContentParser#commandsParser() + */ public void commandsParser() throws IOException { ArrayList<Message> newEmails = new ArrayList<Message>(); ArrayList<Long> deletedEmails = new ArrayList<Long>(); diff --git a/src/com/android/exchange/adapter/EasFolderSyncParser.java b/src/com/android/exchange/adapter/EasFolderSyncParser.java index 639e59df..9760ac20 100644 --- a/src/com/android/exchange/adapter/EasFolderSyncParser.java +++ b/src/com/android/exchange/adapter/EasFolderSyncParser.java @@ -17,12 +17,6 @@ package com.android.exchange.adapter; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import com.android.email.provider.EmailProvider; import com.android.exchange.Eas; import com.android.exchange.EasSyncService; @@ -41,6 +35,12 @@ import android.database.Cursor; import android.os.RemoteException; import android.util.Log; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * Parse the result of a FolderSync command * diff --git a/src/com/android/exchange/adapter/EasOutboxService.java b/src/com/android/exchange/adapter/EasOutboxService.java index 461654b8..c2887080 100644 --- a/src/com/android/exchange/adapter/EasOutboxService.java +++ b/src/com/android/exchange/adapter/EasOutboxService.java @@ -46,11 +46,11 @@ public class EasOutboxService extends EasSyncService { public void run() { mThread = Thread.currentThread(); - String uniqueId = android.provider.Settings.System.getString(mContext.getContentResolver(), - android.provider.Settings.System.ANDROID_ID); + String uniqueId = android.provider.Settings.Secure.getString(mContext.getContentResolver(), + android.provider.Settings.Secure.ANDROID_ID); try { Cursor c = mContext.getContentResolver().query(Message.CONTENT_URI, - Message.CONTENT_PROJECTION, MessageColumns.MAILBOX_KEY + '=' + mMailbox, + Message.CONTENT_PROJECTION, MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null); try { while (c.moveToNext()) { @@ -67,8 +67,7 @@ public class EasOutboxService extends EasSyncService { } else { ContentValues cv = new ContentValues(); cv.put("uid", 1); - Message.update(mContext, - Message.CONTENT_URI, msg.mId, cv); + Message.update(mContext, Message.CONTENT_URI, msg.mId, cv); } // TODO How will the user know that the message sent or not? } diff --git a/src/com/android/exchange/adapter/EasTags.java b/src/com/android/exchange/adapter/EasTags.java index 4a832e10..0f330e5b 100644 --- a/src/com/android/exchange/adapter/EasTags.java +++ b/src/com/android/exchange/adapter/EasTags.java @@ -442,9 +442,10 @@ public class EasTags { { // 0x11 AirSyncBase "BodyPreference", "BodyPreferenceType", "BodyPreferenceTruncationSize", "AllOrNone", - "Body", "Data", "EstimatedDataSize", "Truncated", "Attachments", "Attachment", - "DisplayName", "FileReference", "Method", "ContentId", "ContentLocation", "IsInline", - "NativeBodyType", "ContentType" + "--unused--", "BaseBody", "BaseData", "BaseEstimatedDataSize", "BaseTruncated", + "BaseAttachments", "BaseAttachment", "BaseDisplayName", "FileReference", "BaseMethod", + "BaseContentId", "BaseContentLocation", "BaseIsInline", "BaseNativeBodyType", + "BaseContentType" }, { // 0x12 Settings diff --git a/src/com/android/exchange/utility/Rfc822Formatter.java b/src/com/android/exchange/utility/Rfc822Formatter.java index 55088072..6ea5f38a 100644 --- a/src/com/android/exchange/utility/Rfc822Formatter.java +++ b/src/com/android/exchange/utility/Rfc822Formatter.java @@ -28,6 +28,7 @@ import java.util.Date; import com.android.exchange.EmailContent.Account; import com.android.exchange.EmailContent.Attachment; +import com.android.exchange.EmailContent.Body; import com.android.exchange.EmailContent.Message; import android.content.ContentUris; @@ -36,7 +37,6 @@ import android.database.Cursor; import android.net.Uri; import android.text.Html; import android.text.SpannedString; -import android.util.Log; /** * Generates RFC822 formatted message data from a Message object. This functionality is also needed @@ -67,17 +67,17 @@ public class Rfc822Formatter { // For now, multi-part alternative means an HTML reply... boolean alternativeParts = false; - Uri u = ContentUris.withAppendedId(Message.CONTENT_URI, msg.mId) - .buildUpon().appendPath("attachment").build(); + Uri u = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, msg.mId); Cursor c = context.getContentResolver().query(u, Attachment.CONTENT_PROJECTION, null, null, null); try { - if (c.moveToFirst()) - Log.v("Rfc822", "Has attachments"); + if (c.moveToFirst()) { + // Loop through attachments now + } } finally { c.close(); } - //**PROVIDER + boolean mixedParts = false; //(!msg.attachments.isEmpty()); Message reply = null; boolean forward = false; @@ -143,8 +143,7 @@ public class Rfc822Formatter { writeHeader(writer, "Content-Transfer-Encoding", "7bit"); writer.write(CRLF); - //String text = Messages.getBody(context, msg); - String text = "Fake body (for now)"; + String text = Body.restoreBodyTextWithMessageId(context, msg.mId); writeWithCRLF(writer, text); if (alternativeParts) { |