summaryrefslogtreecommitdiffstats
path: root/src/com/android/exchange
diff options
context:
space:
mode:
authorMarc Blank <mblank@google.com>2009-07-17 16:29:35 -0700
committerMarc Blank <mblank@google.com>2009-07-18 23:31:39 -0700
commit00d91b2e12d65df06916afdc4bebca67fd27214c (patch)
tree45dfb48105504b9454158321f541e07b3b6d2b3b /src/com/android/exchange
parent392971282488b1c5a298b056e38bba148ee294c9 (diff)
downloadandroid_packages_apps_Exchange-00d91b2e12d65df06916afdc4bebca67fd27214c.tar.gz
android_packages_apps_Exchange-00d91b2e12d65df06916afdc4bebca67fd27214c.tar.bz2
android_packages_apps_Exchange-00d91b2e12d65df06916afdc4bebca67fd27214c.zip
Changes to EAS: attachments, HTML text, and sending mail (without atts)
* Added HTML support for Exchange 2007 and later * Modified MessageView to show HTML parts of body (if present) * Replace some unused convenience methods in Body to be ones that are more useful (i.e. retrive body for a given message id) * Made EAS sending operational * Updated SyncManager to recognize the creation of new messages and check for work to be done (i.e. starting Outbox sync) * Modified MessageView to remove EmailContent.X references * Also changed the pattern EmailContent.getContent(cursor, class) to the better new ClassName().restore(cursor) * Add a test of the utility to determine mime type from file name (since EAS doesn't provide this with attachment header information) * Fixed bug in ProviderTestUtils in which the base (EmailContent) fields weren't being checked for equality in the various subclass equality testers. Fixed a bug in saving emails that was caught by fixing this bug.
Diffstat (limited to 'src/com/android/exchange')
-rw-r--r--src/com/android/exchange/Eas.java22
-rw-r--r--src/com/android/exchange/EasSyncService.java83
-rw-r--r--src/com/android/exchange/EmailContent.java135
-rw-r--r--src/com/android/exchange/SyncManager.java42
-rw-r--r--src/com/android/exchange/adapter/EasEmailSyncAdapter.java110
-rw-r--r--src/com/android/exchange/adapter/EasFolderSyncParser.java12
-rw-r--r--src/com/android/exchange/adapter/EasOutboxService.java9
-rw-r--r--src/com/android/exchange/adapter/EasTags.java7
-rw-r--r--src/com/android/exchange/utility/Rfc822Formatter.java15
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) {