Merge "Implement checks before performing background attachment download" into honeycomb

This commit is contained in:
Marc Blank 2011-01-19 22:40:35 -08:00 committed by Android (Google) Code Review
commit c0a8b2cfb0
2 changed files with 76 additions and 43 deletions

View File

@ -24,6 +24,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.net.Uri; import android.net.Uri;
@ -37,18 +38,41 @@ import java.util.List;
* based on the attachment's filename and mime type. * based on the attachment's filename and mime type.
*/ */
public class AttachmentInfo { public class AttachmentInfo {
// Projection which can be used with the constructor taking a Cursor argument
public static final String[] PROJECTION = new String[] {Attachment.RECORD_ID, Attachment.SIZE,
Attachment.FILENAME, Attachment.MIME_TYPE, Attachment.ACCOUNT_KEY};
// Offsets into PROJECTION
public static final int COLUMN_ID = 0;
public static final int COLUMN_SIZE = 1;
public static final int COLUMN_FILENAME = 2;
public static final int COLUMN_MIME_TYPE = 3;
public static final int COLUMN_ACCOUNT_KEY = 4;
public final long mId;
public final long mSize;
public final String mName; public final String mName;
public final String mContentType; public final String mContentType;
public final long mSize; public final long mAccountKey;
public final long mId;
public final boolean mAllowView; public final boolean mAllowView;
public final boolean mAllowSave; public final boolean mAllowSave;
public AttachmentInfo(Context context, Attachment attachment) { public AttachmentInfo(Context context, Attachment attachment) {
mSize = attachment.mSize; this(context, attachment.mId, attachment.mSize, attachment.mFileName, attachment.mMimeType,
mContentType = AttachmentProvider.inferMimeType(attachment.mFileName, attachment.mMimeType); attachment.mAccountKey);
mName = attachment.mFileName; }
mId = attachment.mId;
public AttachmentInfo(Context context, Cursor c) {
this(context, c.getLong(COLUMN_ID), c.getLong(COLUMN_SIZE), c.getString(COLUMN_FILENAME),
c.getString(COLUMN_MIME_TYPE), c.getLong(COLUMN_ACCOUNT_KEY));
}
public AttachmentInfo(Context context, long id, long size, String fileName, String mimeType,
long accountKey) {
mSize = size;
mContentType = AttachmentProvider.inferMimeType(fileName, mimeType);
mName = fileName;
mId = id;
mAccountKey = accountKey;
boolean canView = true; boolean canView = true;
boolean canSave = true; boolean canSave = true;
@ -121,7 +145,11 @@ public class AttachmentInfo {
* An attachment is eligible for download if it can either be viewed or saved (or both) * An attachment is eligible for download if it can either be viewed or saved (or both)
* @return whether the attachment is eligible for download * @return whether the attachment is eligible for download
*/ */
public boolean eligibleForDownload() { public boolean isEligibleForDownload() {
return mAllowView || mAllowSave; return mAllowView || mAllowSave;
} }
public String toString() {
return "{Attachment " + mId + ":" + mName + "," + mContentType + "," + mSize + "}";
}
} }

View File

@ -16,12 +16,13 @@
package com.android.email.service; package com.android.email.service;
import com.android.email.AttachmentInfo;
import com.android.email.Controller.ControllerService;
import com.android.email.Email; import com.android.email.Email;
import com.android.email.ExchangeUtils.NullEmailService;
import com.android.email.NotificationController; import com.android.email.NotificationController;
import com.android.email.Preferences; import com.android.email.Preferences;
import com.android.email.Utility; import com.android.email.Utility;
import com.android.email.Controller.ControllerService;
import com.android.email.ExchangeUtils.NullEmailService;
import com.android.email.provider.AttachmentProvider; import com.android.email.provider.AttachmentProvider;
import com.android.email.provider.EmailContent; import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account; import com.android.email.provider.EmailContent.Account;
@ -83,9 +84,8 @@ public class AttachmentDownloadService extends Service implements Runnable {
// Limit on the number of simultaneous downloads per account // Limit on the number of simultaneous downloads per account
// Note that a limit of 1 is currently enforced by both Services (MailService and Controller) // Note that a limit of 1 is currently enforced by both Services (MailService and Controller)
private static final int MAX_SIMULTANEOUS_DOWNLOADS_PER_ACCOUNT = 1; private static final int MAX_SIMULTANEOUS_DOWNLOADS_PER_ACCOUNT = 1;
// Limit on the number of attachments we'll check for background download
private static final Uri SINGLE_ATTACHMENT_URI = private static final int MAX_ATTACHMENTS_TO_CHECK = 25;
EmailContent.uriWithLimit(Attachment.CONTENT_URI, 1);
/*package*/ static AttachmentDownloadService sRunningService = null; /*package*/ static AttachmentDownloadService sRunningService = null;
@ -319,42 +319,41 @@ public class AttachmentDownloadService extends Service implements Runnable {
} }
} }
// Respect the user's preference for background downloads
if (!mPreferences.getBackgroundAttachments()) {
return;
}
// Then, try opportunistic download of appropriate attachments // Then, try opportunistic download of appropriate attachments
int backgroundDownloads = MAX_SIMULTANEOUS_DOWNLOADS - mDownloadsInProgress.size(); int backgroundDownloads = MAX_SIMULTANEOUS_DOWNLOADS - mDownloadsInProgress.size();
// Always leave one slot for user requested download // Always leave one slot for user requested download
if (backgroundDownloads > (MAX_SIMULTANEOUS_DOWNLOADS - 1)) { if (backgroundDownloads > (MAX_SIMULTANEOUS_DOWNLOADS - 1)) {
boolean repeat = true; // We'll load up the newest 25 attachments that aren't loaded or queued
while (repeat) { Uri lookupUri = EmailContent.uriWithLimit(Attachment.CONTENT_URI,
// We'll take the most recent unloaded attachment MAX_ATTACHMENTS_TO_CHECK);
Long prefetchId = Utility.getFirstRowLong(mContext, SINGLE_ATTACHMENT_URI, Cursor c = mContext.getContentResolver().query(lookupUri, AttachmentInfo.PROJECTION,
Attachment.ID_PROJECTION, AttachmentColumns.CONTENT_URI AttachmentColumns.CONTENT_URI + " isnull AND " + Attachment.FLAGS + "=0",
+ " isnull AND " + Attachment.FLAGS + "=0", null, null, Attachment.RECORD_ID + " DESC");
Attachment.RECORD_ID + " DESC", Attachment.ID_PROJECTION_COLUMN); File cacheDir = mContext.getCacheDir();
if (prefetchId == null) break; try {
if (Email.DEBUG) { while (c.moveToNext()) {
Log.d(TAG, ">> Prefetch attachment " + prefetchId); long accountKey = c.getLong(AttachmentInfo.COLUMN_ACCOUNT_KEY);
} long id = c.getLong(AttachmentInfo.COLUMN_ID);
Attachment att = Attachment.restoreAttachmentWithId(mContext, prefetchId); if (getServiceClassForAccount(accountKey) == null) {
// If att is null, the attachment must have been deleted out from under us // Clean up this orphaned attachment; there's no point in keeping it
if (att == null) continue; // around; then try to find another one
if (getServiceClassForAccount(att.mAccountKey) == null) { EmailContent.delete(mContext, Attachment.CONTENT_URI, id);
// Clean up this orphaned attachment; there's no point in keeping it } else if (canPrefetchForAccount(accountKey, cacheDir)) {
// around; then try to find another one // Check that the attachment meets system requirements for download
EmailContent.delete(mContext, Attachment.CONTENT_URI, prefetchId); AttachmentInfo info = new AttachmentInfo(mContext, c);
continue; if (info.isEligibleForDownload()) {
} Attachment att = Attachment.restoreAttachmentWithId(mContext, id);
repeat = false; if (att != null) {
// TODO It's possible that we're just over limit for this particular account // Start this download and we're done
// Handle this so that attachments from other accounts (if any) can be tried DownloadRequest req = new DownloadRequest(mContext, att);
if (canPrefetchForAccount(att.mAccountKey, mContext.getCacheDir())) { mDownloadSet.tryStartDownload(req);
DownloadRequest req = new DownloadRequest(mContext, att); break;
mDownloadSet.tryStartDownload(req); }
}
}
} }
} finally {
c.close();
} }
} }
} }
@ -745,6 +744,12 @@ public class AttachmentDownloadService extends Service implements Runnable {
* @return true if download is allowed, false otherwise * @return true if download is allowed, false otherwise
*/ */
/*package*/ boolean canPrefetchForAccount(long accountId, File dir) { /*package*/ boolean canPrefetchForAccount(long accountId, File dir) {
Account account = Account.restoreAccountWithId(mContext, accountId);
// Check account, just in case
if (account == null) return false;
// First, check preference and quickly return if prefetch isn't allowed
if ((account.mFlags & Account.FLAGS_BACKGROUND_ATTACHMENTS) == 0) return false;
long totalStorage = dir.getTotalSpace(); long totalStorage = dir.getTotalSpace();
long usableStorage = dir.getUsableSpace(); long usableStorage = dir.getUsableSpace();
long minAvailable = (long)(totalStorage * PREFETCH_MINIMUM_STORAGE_AVAILABLE); long minAvailable = (long)(totalStorage * PREFETCH_MINIMUM_STORAGE_AVAILABLE);