Add support for attachments in EmailProvider and (preliminary) EAS
* EmailProvider now saves Attachment records atomically with Message (and Body, of course) if an ArrayList is stored in mAttachments * Update EAS code to support attachment discovery * Update EAS code to support attachment download via service API (preliminary) * Add test for atomic attachment save * Add test for unique file creation (external)
This commit is contained in:
parent
c5f783f022
commit
976f92908d
@ -26,10 +26,12 @@ import android.content.Context;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
@ -638,7 +640,7 @@ public abstract class EmailContent {
|
||||
|
||||
public void addSaveOps(ArrayList<ContentProviderOperation> ops) {
|
||||
// First, save the message
|
||||
ContentProviderOperation.Builder b = getSaveOrUpdateBuilder(true, mBaseUri, mId);
|
||||
ContentProviderOperation.Builder b = getSaveOrUpdateBuilder(true, mBaseUri, -1);
|
||||
ops.add(b.withValues(toContentValues()).build());
|
||||
|
||||
// Create and save the body
|
||||
@ -652,8 +654,19 @@ public abstract class EmailContent {
|
||||
b = getSaveOrUpdateBuilder(true, Body.CONTENT_URI, 0);
|
||||
b.withValues(cv);
|
||||
ContentValues backValues = new ContentValues();
|
||||
backValues.put(Body.MESSAGE_KEY, ops.size() - 1);
|
||||
int messageBackValue = ops.size() - 1;
|
||||
backValues.put(Body.MESSAGE_KEY, messageBackValue);
|
||||
ops.add(b.withValueBackReferences(backValues).build());
|
||||
|
||||
// Create the attaachments, if any
|
||||
if (mAttachments != null) {
|
||||
for (Attachment att: mAttachments) {
|
||||
ops.add(getSaveOrUpdateBuilder(true, Attachment.CONTENT_URI, -1)
|
||||
.withValues(att.toContentValues())
|
||||
.withValueBackReference(Attachment.MESSAGE_KEY, messageBackValue)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Text and Html information are stored as <location>;<encoding>;<charset>;<length>
|
||||
@ -1482,6 +1495,39 @@ public abstract class EmailContent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a unique file in the external store by appending a hyphen
|
||||
* and a number to the given filename.
|
||||
* @param filename
|
||||
* @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);
|
||||
if (!file.exists()) {
|
||||
return file;
|
||||
}
|
||||
// Get the extension of the file, if any.
|
||||
int index = filename.lastIndexOf('.');
|
||||
String name = filename;
|
||||
String extension = "";
|
||||
if (index != -1) {
|
||||
name = filename.substring(0, index);
|
||||
extension = filename.substring(index);
|
||||
}
|
||||
for (int i = 2; i < Integer.MAX_VALUE; i++) {
|
||||
file = new File(directory, name + '-' + i + extension);
|
||||
if (!file.exists()) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public EmailContent.Attachment restore(Cursor cursor) {
|
||||
|
@ -69,8 +69,8 @@ public abstract class AbstractSyncService implements Runnable {
|
||||
protected String mMailboxName;
|
||||
public Account mAccount;
|
||||
protected Context mContext;
|
||||
protected long mRequestTime = 0;
|
||||
|
||||
protected volatile long mRequestTime = 0;
|
||||
protected ArrayList<PartRequest> mPartRequests = new ArrayList<PartRequest>();
|
||||
protected PartRequest mPendingPartRequest = null;
|
||||
|
||||
@ -285,6 +285,7 @@ public abstract class AbstractSyncService implements Runnable {
|
||||
public void addPartRequest(PartRequest req) {
|
||||
synchronized (mPartRequests) {
|
||||
mPartRequests.add(req);
|
||||
mRequestTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,7 @@ import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
public class EasSyncService extends InteractiveSyncService {
|
||||
@ -76,8 +77,11 @@ public class EasSyncService extends InteractiveSyncService {
|
||||
private static final String WHERE_SYNC_FREQUENCY_PING =
|
||||
Mailbox.SYNC_FREQUENCY + '=' + Account.CHECK_INTERVAL_PING;
|
||||
private static final String SYNC_FREQUENCY_PING =
|
||||
MailboxColumns.SYNC_FREQUENCY + '=' + Account.CHECK_INTERVAL_PING;
|
||||
MailboxColumns.SYNC_FREQUENCY + " IN (" + Account.CHECK_INTERVAL_PING +
|
||||
',' + Account.CHECK_INTERVAL_PUSH + ')';
|
||||
|
||||
static private final int CHUNK_SIZE = 16 * 1024;
|
||||
|
||||
// Reasonable default
|
||||
String mProtocolVersion = "2.5";
|
||||
static String mDeviceId = null;
|
||||
@ -195,7 +199,7 @@ public class EasSyncService extends InteractiveSyncService {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
protected HttpURLConnection sendEASPostCommand(String cmd, String data) throws IOException {
|
||||
protected HttpURLConnection sendEASPostCommand(String cmd, String data) throws IOException {
|
||||
HttpURLConnection uc = setupEASCommand("POST", cmd);
|
||||
if (uc != null) {
|
||||
uc.setRequestProperty("Content-Length", Integer.toString(data.length() + 2));
|
||||
@ -208,11 +212,36 @@ public class EasSyncService extends InteractiveSyncService {
|
||||
return uc;
|
||||
}
|
||||
|
||||
static private final int CHUNK_SIZE = 16 * 1024;
|
||||
private void doStatusCallback(IEmailServiceCallback callback, int status) {
|
||||
try {
|
||||
callback.status(status, 0);
|
||||
} catch (RemoteException e2) {
|
||||
// No danger if the client is no longer around
|
||||
}
|
||||
}
|
||||
|
||||
protected void getAttachment(PartRequest req) throws IOException {
|
||||
private void doProgressCallback(IEmailServiceCallback callback, int progress) {
|
||||
try {
|
||||
callback.status(EmailServiceStatus.IN_PROGRESS, progress);
|
||||
} catch (RemoteException e2) {
|
||||
// No danger if the client is no longer around
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an attachment, based on the PartRequest passed in. The PartRequest is basically our
|
||||
* wrapper for Attachment
|
||||
* @param req the part (attachment) to be retrieved
|
||||
* @param external whether the attachment should be loaded to external storage
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void getAttachment(PartRequest req, boolean external) throws IOException {
|
||||
// TODO Implement internal storage as required
|
||||
IEmailServiceCallback callback = req.callback;
|
||||
Attachment att = req.att;
|
||||
doProgressCallback(callback, 0);
|
||||
DefaultHttpClient client = new DefaultHttpClient();
|
||||
String us = makeUriString("GetAttachment", "&AttachmentName=" + req.att.mLocation);
|
||||
String us = makeUriString("GetAttachment", "&AttachmentName=" + att.mLocation);
|
||||
HttpPost method = new HttpPost(URI.create(us));
|
||||
method.setHeader("Authorization", mAuthString);
|
||||
|
||||
@ -226,8 +255,7 @@ public class EasSyncService extends InteractiveSyncService {
|
||||
Log.v(TAG, "Attachment code: " + status + ", Length: " + len + ", Type: " + type);
|
||||
}
|
||||
InputStream is = res.getEntity().getContent();
|
||||
// TODO Use the request data, when it's defined. For now, stubbed out
|
||||
File f = null; // Attachment.openAttachmentFile(req);
|
||||
File f = Attachment.createUniqueFile(att.mFileName);
|
||||
if (f != null) {
|
||||
FileOutputStream os = new FileOutputStream(f);
|
||||
if (len > 0) {
|
||||
@ -241,10 +269,8 @@ public class EasSyncService extends InteractiveSyncService {
|
||||
int read = is.read(bytes, 0, n);
|
||||
os.write(bytes, 0, read);
|
||||
len -= read;
|
||||
if (req.handler != null) {
|
||||
long pct = ((length - len) * 100 / length);
|
||||
req.handler.sendEmptyMessage((int)pct);
|
||||
}
|
||||
int pct = ((length - len) * 100 / length);
|
||||
doProgressCallback(callback, pct);
|
||||
}
|
||||
} finally {
|
||||
mPendingPartRequest = null;
|
||||
@ -254,12 +280,17 @@ public class EasSyncService extends InteractiveSyncService {
|
||||
os.flush();
|
||||
os.close();
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(AttachmentColumns.CONTENT_URI, f.getAbsolutePath());
|
||||
cv.put(AttachmentColumns.MIME_TYPE, type);
|
||||
req.att.update(mContext, cv);
|
||||
// TODO Inform UI that we're done
|
||||
// EmailProvider will throw an exception if we try to update an unsaved attachment
|
||||
if (att.isSaved()) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(AttachmentColumns.CONTENT_URI, f.getAbsolutePath());
|
||||
cv.put(AttachmentColumns.MIME_TYPE, type);
|
||||
att.update(mContext, cv);
|
||||
doStatusCallback(callback, EmailServiceStatus.SUCCESS);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
doStatusCallback(callback, EmailServiceStatus.MESSAGE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,11 +439,14 @@ public class EasSyncService extends InteractiveSyncService {
|
||||
}
|
||||
|
||||
// Wait for push notifications.
|
||||
String threadName = Thread.currentThread().getName();
|
||||
try {
|
||||
runPingLoop();
|
||||
} catch (StaleFolderListException e) {
|
||||
// We break out if we get told about a stale folder list
|
||||
userLog("Ping interrupted; folder list requires sync...");
|
||||
} finally {
|
||||
Thread.currentThread().setName(threadName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -471,6 +505,7 @@ public class EasSyncService extends InteractiveSyncService {
|
||||
// If we have some number that are ready for push, send Ping to the server
|
||||
s.end("PingFolders").end("Ping").end();
|
||||
uc = sendEASPostCommand("Ping", s.toString());
|
||||
Thread.currentThread().setName(mAccount.mDisplayName + ": Ping");
|
||||
userLog("Sending ping, timeout: " + uc.getReadTimeout() / 1000 + "s");
|
||||
code = uc.getResponseCode();
|
||||
userLog("Ping response: " + code);
|
||||
@ -521,7 +556,7 @@ public class EasSyncService extends InteractiveSyncService {
|
||||
mBindArguments[0] = Long.toString(mAccount.mId);
|
||||
ArrayList<String> syncList = pp.getSyncList();
|
||||
for (String serverId: syncList) {
|
||||
mBindArguments[1] = serverId;
|
||||
mBindArguments[1] = serverId;
|
||||
Cursor c = cr.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
|
||||
WHERE_ACCOUNT_KEY_AND_SERVER_ID, mBindArguments, null);
|
||||
try {
|
||||
@ -626,6 +661,21 @@ public class EasSyncService extends InteractiveSyncService {
|
||||
runAwake();
|
||||
waitForConnectivity();
|
||||
|
||||
while (true) {
|
||||
PartRequest req = null;
|
||||
synchronized (mPartRequests) {
|
||||
if (mPartRequests.isEmpty()) {
|
||||
break;
|
||||
} else {
|
||||
req = mPartRequests.get(0);
|
||||
}
|
||||
}
|
||||
getAttachment(req, true);
|
||||
synchronized(mPartRequests) {
|
||||
mPartRequests.remove(req);
|
||||
}
|
||||
}
|
||||
|
||||
EasSerializer s = new EasSerializer();
|
||||
if (mailbox.mSyncKey == null) {
|
||||
userLog("Mailbox syncKey RESET");
|
||||
|
@ -13,15 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.exchange;
|
||||
|
||||
/**
|
||||
* This is a local copy of com.android.email.EmailProvider
|
||||
*
|
||||
* Last copied from com.android.email.EmailProvider on 7/15/09
|
||||
* Last copied from com.android.email.EmailProvider on 7/16/09
|
||||
*/
|
||||
|
||||
package com.android.exchange;
|
||||
|
||||
import com.android.email.R;
|
||||
import com.android.email.provider.EmailProvider;
|
||||
|
||||
@ -33,10 +32,12 @@ import android.content.Context;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
@ -647,10 +648,10 @@ public abstract class EmailContent {
|
||||
}
|
||||
|
||||
public void addSaveOps(ArrayList<ContentProviderOperation> ops) {
|
||||
// First, save the message
|
||||
ContentProviderOperation.Builder b = getSaveOrUpdateBuilder(true, mBaseUri, mId);
|
||||
// First, save the message
|
||||
ContentProviderOperation.Builder b = getSaveOrUpdateBuilder(true, mBaseUri, -1);
|
||||
ops.add(b.withValues(toContentValues()).build());
|
||||
|
||||
|
||||
// Create and save the body
|
||||
ContentValues cv = new ContentValues();
|
||||
if (mText != null) {
|
||||
@ -662,9 +663,20 @@ public abstract class EmailContent {
|
||||
b = getSaveOrUpdateBuilder(true, Body.CONTENT_URI, 0);
|
||||
b.withValues(cv);
|
||||
ContentValues backValues = new ContentValues();
|
||||
backValues.put(Body.MESSAGE_KEY, ops.size() - 1);
|
||||
int messageBackValue = ops.size() - 1;
|
||||
backValues.put(Body.MESSAGE_KEY, messageBackValue);
|
||||
ops.add(b.withValueBackReferences(backValues).build());
|
||||
}
|
||||
|
||||
// Create the attaachments, if any
|
||||
if (mAttachments != null) {
|
||||
for (Attachment att: mAttachments) {
|
||||
ops.add(getSaveOrUpdateBuilder(true, Attachment.CONTENT_URI, -1)
|
||||
.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)
|
||||
@ -1492,6 +1504,38 @@ public abstract class EmailContent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a unique file in the external store by appending a hyphen
|
||||
* and a number to the given filename.
|
||||
* @param filename
|
||||
* @return a new File object, or null if one could not be created
|
||||
*/
|
||||
public static File createUniqueFile(String filename) {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
File directory = Environment.getExternalStorageDirectory();
|
||||
File file = new File(directory, filename);
|
||||
if (!file.exists()) {
|
||||
return file;
|
||||
}
|
||||
// Get the extension of the file, if any.
|
||||
int index = filename.lastIndexOf('.');
|
||||
String name = filename;
|
||||
String extension = "";
|
||||
if (index != -1) {
|
||||
name = filename.substring(0, index);
|
||||
extension = filename.substring(index);
|
||||
}
|
||||
for (int i = 2; i < Integer.MAX_VALUE; i++) {
|
||||
file = new File(directory, name + '-' + i + extension);
|
||||
if (!file.exists()) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public EmailContent.Attachment restore(Cursor cursor) {
|
||||
|
@ -17,30 +17,51 @@
|
||||
|
||||
package com.android.exchange;
|
||||
|
||||
import com.android.email.provider.EmailContent;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import android.os.Handler;
|
||||
import com.android.exchange.EmailContent.Attachment;
|
||||
|
||||
/**
|
||||
* PartRequest is the EAS wrapper for attachment loading requests. In addition to information about
|
||||
* the attachment to be loaded, it also contains the callback to be used for status/progress
|
||||
* updates to the UI.
|
||||
*/
|
||||
public class PartRequest {
|
||||
public long id;
|
||||
public long timeStamp;
|
||||
public long emailId;
|
||||
public EmailContent.Attachment att;
|
||||
public Attachment att;
|
||||
public String loc;
|
||||
public long size;
|
||||
public long loaded;
|
||||
public Handler handler;
|
||||
public IEmailServiceCallback callback;
|
||||
|
||||
public PartRequest (long _emailId, EmailContent.Attachment _att) {
|
||||
id = System.currentTimeMillis();
|
||||
static IEmailServiceCallback sCallback = new IEmailServiceCallback () {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.android.exchange.IEmailServiceCallback#status(int, int)
|
||||
*/
|
||||
public void status(int statusCode, int progress) throws RemoteException {
|
||||
// This is a placeholder, so that all PartRequests have a callback (prevents a lot of
|
||||
// useless checking in the sync service). When debugging, logs the status and progress
|
||||
// of the download.
|
||||
if (Eas.TEST_DEBUG) {
|
||||
Log.d("Status: ", "Code = " + statusCode + ", progress = " + progress);
|
||||
}
|
||||
}
|
||||
|
||||
public IBinder asBinder() { return null; }
|
||||
};
|
||||
|
||||
public PartRequest(long _emailId, Attachment _att) {
|
||||
timeStamp = System.currentTimeMillis();
|
||||
emailId = _emailId;
|
||||
att = _att;
|
||||
loc = att.mLocation;
|
||||
size = att.mSize;
|
||||
loaded = 0;
|
||||
callback = sCallback;
|
||||
}
|
||||
|
||||
public PartRequest (long _emailId, EmailContent.Attachment _att, Handler _handler) {
|
||||
public PartRequest(long _emailId, Attachment _att, IEmailServiceCallback _callback) {
|
||||
this(_emailId, _att);
|
||||
handler = _handler;
|
||||
callback = _callback;
|
||||
}
|
||||
}
|
||||
|
@ -568,9 +568,10 @@ public class SyncManager extends Service implements Runnable {
|
||||
}
|
||||
|
||||
public void run() {
|
||||
log("Running");
|
||||
mStop = false;
|
||||
|
||||
//Debug.waitForDebugger(); // DON'T CHECK IN WITH THIS
|
||||
|
||||
runAwake(-1);
|
||||
|
||||
ContentResolver resolver = getContentResolver();
|
||||
@ -762,7 +763,6 @@ public class SyncManager extends Service implements Runnable {
|
||||
}
|
||||
|
||||
if (service != null) {
|
||||
service.mRequestTime = System.currentTimeMillis();
|
||||
service.addPartRequest(req);
|
||||
kick();
|
||||
}
|
||||
@ -776,7 +776,6 @@ public class SyncManager extends Service implements Runnable {
|
||||
long mailboxId = msg.mMailboxKey;
|
||||
AbstractSyncService service = INSTANCE.mServiceMap.get(mailboxId);
|
||||
if (service != null) {
|
||||
service.mRequestTime = System.currentTimeMillis();
|
||||
return service.hasPartRequest(emailId, part);
|
||||
}
|
||||
return null;
|
||||
@ -790,7 +789,6 @@ public class SyncManager extends Service implements Runnable {
|
||||
long mailboxId = msg.mMailboxKey;
|
||||
AbstractSyncService service = INSTANCE.mServiceMap.get(mailboxId);
|
||||
if (service != null) {
|
||||
service.mRequestTime = System.currentTimeMillis();
|
||||
service.cancelPartRequest(emailId, part);
|
||||
}
|
||||
}
|
||||
|
@ -103,9 +103,7 @@ public class EasEmailSyncAdapter extends EasSyncAdapter {
|
||||
while (nextTag(EasTags.SYNC_APPLICATION_DATA) != END) {
|
||||
switch (tag) {
|
||||
case EasTags.EMAIL_ATTACHMENTS:
|
||||
break;
|
||||
case EasTags.EMAIL_ATTACHMENT:
|
||||
attachmentParser(atts, msg);
|
||||
attachmentsParser(atts, msg);
|
||||
break;
|
||||
case EasTags.EMAIL_TO:
|
||||
to = getValue();
|
||||
@ -194,7 +192,7 @@ public class EasEmailSyncAdapter extends EasSyncAdapter {
|
||||
public void attachmentParser(ArrayList<Attachment> atts, Message msg) throws IOException {
|
||||
String fileName = null;
|
||||
String length = null;
|
||||
String lvl = null;
|
||||
String location = null;
|
||||
|
||||
while (nextTag(EasTags.EMAIL_ATTACHMENT) != END) {
|
||||
switch (tag) {
|
||||
@ -202,7 +200,7 @@ public class EasEmailSyncAdapter extends EasSyncAdapter {
|
||||
fileName = getValue();
|
||||
break;
|
||||
case EasTags.EMAIL_ATT_NAME:
|
||||
lvl = getValue();
|
||||
location = getValue();
|
||||
break;
|
||||
case EasTags.EMAIL_ATT_SIZE:
|
||||
length = getValue();
|
||||
@ -212,16 +210,29 @@ public class EasEmailSyncAdapter extends EasSyncAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
if (fileName != null && length != null && lvl != null) {
|
||||
if (fileName != null && length != null && location != null) {
|
||||
Attachment att = new Attachment();
|
||||
att.mEncoding = "base64";
|
||||
att.mSize = Long.parseLong(length);
|
||||
att.mFileName = fileName;
|
||||
att.mLocation = location;
|
||||
atts.add(att);
|
||||
msg.mFlagAttachment = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void attachmentsParser(ArrayList<Attachment> atts, Message msg) throws IOException {
|
||||
while (nextTag(EasTags.EMAIL_ATTACHMENTS) != END) {
|
||||
switch (tag) {
|
||||
case EasTags.EMAIL_ATTACHMENT:
|
||||
attachmentParser(atts, msg);
|
||||
break;
|
||||
default:
|
||||
skipTag();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Cursor getServerIdCursor(String serverId, String[] projection) {
|
||||
bindArguments[0] = serverId;
|
||||
bindArguments[1] = mMailboxIdAsString;
|
||||
|
@ -16,7 +16,13 @@
|
||||
|
||||
package com.android.email.provider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
import com.android.email.provider.EmailContent.Attachment;
|
||||
import com.android.email.provider.EmailContent.Body;
|
||||
import com.android.email.provider.EmailContent.Mailbox;
|
||||
import com.android.email.provider.EmailContent.Message;
|
||||
@ -28,6 +34,7 @@ import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.test.ProviderTestCase2;
|
||||
|
||||
/**
|
||||
@ -87,10 +94,22 @@ public class ProviderTests extends ProviderTestCase2<EmailProvider> {
|
||||
ProviderTestUtils.assertMailboxEqual("testMailboxSave", box1, box2);
|
||||
}
|
||||
|
||||
private static Attachment setupAttachment(String fileName, long length) {
|
||||
Attachment att = new Attachment();
|
||||
att.mFileName = fileName;
|
||||
att.mLocation = "location" + length;
|
||||
att.mSize = length;
|
||||
return att;
|
||||
}
|
||||
|
||||
public static String[] expectedAttachmentNames =
|
||||
new String[] {"attachment1.doc", "attachment2.xls", "attachment3"};
|
||||
// The lengths need to be kept in ascending order
|
||||
public static long[] expectedAttachmentSizes = new long[] {31415L, 97701L, 151213L};
|
||||
|
||||
/**
|
||||
* Test simple message save/retrieve
|
||||
*
|
||||
* TODO: attachments
|
||||
* TODO: serverId vs. serverIntId
|
||||
*/
|
||||
public void testMessageSave() {
|
||||
@ -137,7 +156,45 @@ public class ProviderTests extends ProviderTestCase2<EmailProvider> {
|
||||
assertEquals("body text", text2, body2.mTextContent);
|
||||
assertEquals("body html", html2, body2.mHtmlContent);
|
||||
} finally {
|
||||
if (c != null) c.close();
|
||||
c.close();
|
||||
}
|
||||
|
||||
Message message3 = ProviderTestUtils.setupMessage("message3", account1Id, box1Id, true,
|
||||
false, mMockContext);
|
||||
ArrayList<Attachment> atts = new ArrayList<Attachment>();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
atts.add(setupAttachment(expectedAttachmentNames[i], expectedAttachmentSizes[i]));
|
||||
}
|
||||
message3.mAttachments = atts;
|
||||
message3.saveOrUpdate(mMockContext);
|
||||
long message3Id = message3.mId;
|
||||
|
||||
// Now check the attachments; there should be three and they should match name and size
|
||||
c = null;
|
||||
try {
|
||||
// Note that there is NO guarantee of the order of returned records in the general case,
|
||||
// so we specifically ask for ordering by size. The expectedAttachmentSizes array must
|
||||
// be kept sorted by size (ascending) for this test to work properly
|
||||
c = mMockContext.getContentResolver().query(
|
||||
Attachment.CONTENT_URI,
|
||||
Attachment.CONTENT_PROJECTION,
|
||||
Attachment.MESSAGE_KEY + "=?",
|
||||
new String[] {
|
||||
String.valueOf(message3Id)
|
||||
},
|
||||
Attachment.SIZE);
|
||||
int numAtts = c.getCount();
|
||||
assertEquals(3, numAtts);
|
||||
int i = 0;
|
||||
while (c.moveToNext()) {
|
||||
assertEquals(expectedAttachmentNames[i],
|
||||
c.getString(Attachment.CONTENT_FILENAME_COLUMN));
|
||||
assertEquals(expectedAttachmentSizes[i],
|
||||
c.getLong(Attachment.CONTENT_SIZE_COLUMN));
|
||||
i++;
|
||||
}
|
||||
} finally {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -487,4 +544,47 @@ public class ProviderTests extends ProviderTestCase2<EmailProvider> {
|
||||
* TODO: attachments
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test that our unique file name algorithm works as expected. Since this test requires an
|
||||
* SD card, we check the environment first, and return immediately if none is mounted.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void testCreateUniqueFile() throws IOException {
|
||||
// Delete existing files, if they exist
|
||||
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String fileName = "A11achm3n1.doc";
|
||||
File uniqueFile = Attachment.createUniqueFile(fileName);
|
||||
assertEquals(fileName, uniqueFile.getName());
|
||||
if (uniqueFile.createNewFile()) {
|
||||
uniqueFile = Attachment.createUniqueFile(fileName);
|
||||
assertEquals("A11achm3n1-2.doc", uniqueFile.getName());
|
||||
if (uniqueFile.createNewFile()) {
|
||||
uniqueFile = Attachment.createUniqueFile(fileName);
|
||||
assertEquals("A11achm3n1-3.doc", uniqueFile.getName());
|
||||
}
|
||||
}
|
||||
fileName = "A11achm3n1";
|
||||
uniqueFile = Attachment.createUniqueFile(fileName);
|
||||
assertEquals(fileName, uniqueFile.getName());
|
||||
if (uniqueFile.createNewFile()) {
|
||||
uniqueFile = Attachment.createUniqueFile(fileName);
|
||||
assertEquals("A11achm3n1-2", uniqueFile.getName());
|
||||
}
|
||||
} finally {
|
||||
File directory = Environment.getExternalStorageDirectory();
|
||||
// These are the files that should be created earlier in the test. Make sure
|
||||
// they are deleted for the next go-around
|
||||
String[] fileNames = new String[] {"A11achm3n1.doc", "A11achm3n1-2.doc", "A11achm3n1"};
|
||||
int length = fileNames.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
File file = new File(directory, fileNames[i]);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user