diff options
Diffstat (limited to 'src/com/android')
8 files changed, 151 insertions, 18 deletions
diff --git a/src/com/android/providers/downloads/Constants.java b/src/com/android/providers/downloads/Constants.java index 7b8fcd24..a10cb514 100644 --- a/src/com/android/providers/downloads/Constants.java +++ b/src/com/android/providers/downloads/Constants.java @@ -57,6 +57,9 @@ public class Constants { /** the intent that gets sent when deleting the notification of a completed download */ public static final String ACTION_HIDE = "android.intent.action.DOWNLOAD_HIDE"; + /** the intent that gets sent when choosing to resume the paused download */ + public static final String ACTION_RESUME = "android.intent.action.DOWNLOAD_RESUME"; + /** The default base name for downloaded files if we can't get one at the HTTP level */ public static final String DEFAULT_DL_FILENAME = "downloadfile"; diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java index bee5c4a9..a0a77b43 100644 --- a/src/com/android/providers/downloads/DownloadInfo.java +++ b/src/com/android/providers/downloads/DownloadInfo.java @@ -372,7 +372,8 @@ public class DownloadInfo { if (!Downloads.Impl.isStatusCompleted(mStatus)) { return false; } - if (mVisibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) { + if (mVisibility == Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED || + mVisibility == DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION) { return true; } return false; diff --git a/src/com/android/providers/downloads/DownloadNotifier.java b/src/com/android/providers/downloads/DownloadNotifier.java index 60c249f9..7fbb35b8 100644 --- a/src/com/android/providers/downloads/DownloadNotifier.java +++ b/src/com/android/providers/downloads/DownloadNotifier.java @@ -35,6 +35,7 @@ import android.os.SystemClock; import android.provider.Downloads; import android.text.TextUtils; import android.text.format.DateUtils; +import android.text.format.Formatter; import android.util.Log; import android.util.LongSparseLongArray; @@ -153,9 +154,37 @@ public class DownloadNotifier { } builder.setWhen(firstShown); + // Check paused status about these downloads. If exists, will + // update icon and content title/content text in notification. + boolean hasPausedStatus = false; + int pausedStatus = -1; + for (DownloadInfo info : cluster) { + if (isPausedStatus(info.mStatus)) { + hasPausedStatus = true; + pausedStatus = info.mStatus; + break; + } + } + + // Check error status about downloads. If error exists, will + // update icon and content title/content text in notification. + boolean hasErrorStatus = false; + for (DownloadInfo info : cluster) { + if (isErrorStatus(info.mStatus)) { + hasErrorStatus = true; + break; + } + } + // Show relevant icon if (type == TYPE_ACTIVE) { - builder.setSmallIcon(android.R.drawable.stat_sys_download); + if (hasPausedStatus) { + builder.setSmallIcon(R.drawable.download_pause); + } else if (hasErrorStatus) { + builder.setSmallIcon(R.drawable.download_error); + } else { + builder.setSmallIcon(android.R.drawable.stat_sys_download); + } } else if (type == TYPE_WAITING) { builder.setSmallIcon(android.R.drawable.stat_sys_warning); } else if (type == TYPE_COMPLETE) { @@ -205,9 +234,10 @@ public class DownloadNotifier { // Calculate and show progress String remainingText = null; String percentText = null; + String speedAsSizeText = null; + long total = 0; if (type == TYPE_ACTIVE) { long current = 0; - long total = 0; long speed = 0; synchronized (mDownloadSpeed) { for (DownloadInfo info : cluster) { @@ -225,8 +255,26 @@ public class DownloadNotifier { if (speed > 0) { final long remainingMillis = ((total - current) * 1000) / speed; + final int duration, durationResId; + + // This duplicates DateUtils.formatDuration(), but uses our + // abbreviated plurals. + if (remainingMillis >= DateUtils.HOUR_IN_MILLIS) { + duration = (int) ((remainingMillis + 1800000) + / DateUtils.HOUR_IN_MILLIS); + durationResId = R.plurals.duration_hours; + } else if (remainingMillis >= DateUtils.MINUTE_IN_MILLIS) { + duration = (int) ((remainingMillis + 30000) + / DateUtils.MINUTE_IN_MILLIS); + durationResId = R.plurals.duration_minutes; + } else { + duration = (int) ((remainingMillis + 500) + / DateUtils.SECOND_IN_MILLIS); + durationResId = R.plurals.duration_seconds; + } remainingText = res.getString(R.string.download_remaining, - DateUtils.formatDuration(remainingMillis)); + res.getQuantityString(durationResId, duration, duration)); + speedAsSizeText = Formatter.formatFileSize(mContext, speed); } final int percent = (int) ((current * 100) / total); @@ -244,10 +292,20 @@ public class DownloadNotifier { builder.setContentTitle(getDownloadTitle(res, info)); if (type == TYPE_ACTIVE) { - if (!TextUtils.isEmpty(info.mDescription)) { - builder.setContentText(info.mDescription); - } else { - builder.setContentText(remainingText); + if (hasPausedStatus) { + if (pausedStatus == Downloads.Impl.STATUS_PAUSED_BY_MANUAL) { + builder.setContentText(res.getText(R.string.download_paused)); + } else { + builder.setContentText(res.getText(R.string.download_queued)); + } + } else if (speedAsSizeText != null) { + builder.setContentText(res.getString(R.string.download_speed_text, + remainingText, speedAsSizeText)); + } else if (hasErrorStatus) { + builder.setContentText(res.getText( + R.string.notification_download_failed)); + if (total == 0) + builder.setProgress(100, 0, false); } builder.setContentInfo(percentText); @@ -274,10 +332,20 @@ public class DownloadNotifier { } if (type == TYPE_ACTIVE) { - builder.setContentTitle(res.getQuantityString( - R.plurals.notif_summary_active, cluster.size(), cluster.size())); + if (hasPausedStatus) { + builder.setContentTitle(res.getString(R.string.download_queued)); + } else if (hasErrorStatus) { + builder.setContentText(res.getText( + R.string.notification_download_failed)); + if (total == 0) + builder.setProgress(100, 0, false); + } else { + builder.setContentTitle(res.getQuantityString( + R.plurals.notif_summary_active, cluster.size(), cluster.size())); + } builder.setContentText(remainingText); - builder.setContentInfo(percentText); + builder.setContentInfo(res.getString(R.string.download_speed_text, + percentText, speedAsSizeText)); inboxStyle.setSummaryText(remainingText); } else if (type == TYPE_WAITING) { @@ -360,7 +428,7 @@ public class DownloadNotifier { } private static boolean isActiveAndVisible(DownloadInfo download) { - return download.mStatus == STATUS_RUNNING && + return Downloads.Impl.isStatusInformational(download.mStatus) && (download.mVisibility == VISIBILITY_VISIBLE || download.mVisibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED); } @@ -370,4 +438,18 @@ public class DownloadNotifier { (download.mVisibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED || download.mVisibility == VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION); } + + private static boolean isPausedStatus(int status) { + return status == Downloads.Impl.STATUS_WAITING_FOR_NETWORK || + status == Downloads.Impl.STATUS_PAUSED_BY_MANUAL; + } + + private static boolean isErrorStatus(int status) { + boolean isErrorStatus = Downloads.Impl.isStatusError(status) + || Downloads.Impl.isStatusClientError(status) + || Downloads.Impl.isStatusServerError(status) + || status == Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR + || status == Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR; + return isErrorStatus; + } } diff --git a/src/com/android/providers/downloads/DownloadReceiver.java b/src/com/android/providers/downloads/DownloadReceiver.java index 28e2a673..d2872356 100644 --- a/src/com/android/providers/downloads/DownloadReceiver.java +++ b/src/com/android/providers/downloads/DownloadReceiver.java @@ -87,7 +87,8 @@ public class DownloadReceiver extends BroadcastReceiver { } }); - } else if (Constants.ACTION_RETRY.equals(action)) { + } else if (Constants.ACTION_RETRY.equals(action) || + Constants.ACTION_RESUME.equals(action)) { startService(context); } else if (Constants.ACTION_OPEN.equals(action) diff --git a/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java index b0b73297..d1190cc1 100644 --- a/src/com/android/providers/downloads/DownloadService.java +++ b/src/com/android/providers/downloads/DownloadService.java @@ -366,7 +366,6 @@ public class DownloadService extends Service { final int idColumn = cursor.getColumnIndexOrThrow(Downloads.Impl._ID); while (cursor.moveToNext()) { final long id = cursor.getLong(idColumn); - staleIds.remove(id); DownloadInfo info = mDownloads.get(id); if (info != null) { @@ -383,6 +382,7 @@ public class DownloadService extends Service { deleteFileIfExists(info.mFileName); resolver.delete(info.getAllDownloadsUri(), null, null); + staleIds.add(info.mId); } else { // Kick off download task if ready @@ -398,6 +398,7 @@ public class DownloadService extends Service { isActive |= activeDownload; isActive |= activeScan; + staleIds.remove(id); } // Keep track of nearest next action diff --git a/src/com/android/providers/downloads/DownloadStorageProvider.java b/src/com/android/providers/downloads/DownloadStorageProvider.java index 1b5dc844..69efab31 100644 --- a/src/com/android/providers/downloads/DownloadStorageProvider.java +++ b/src/com/android/providers/downloads/DownloadStorageProvider.java @@ -303,20 +303,31 @@ public class DownloadStorageProvider extends DocumentsProvider { size = null; } + final long progress = cursor.getLong(cursor.getColumnIndexOrThrow( + DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); final int status = cursor.getInt( cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)); + final int reason = cursor.getInt( + cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON)); switch (status) { case DownloadManager.STATUS_SUCCESSFUL: break; case DownloadManager.STATUS_PAUSED: - summary = getContext().getString(R.string.download_queued); + if (size != null) { + final long percent = progress * 100 / size; + summary = (reason == DownloadManager.PAUSED_BY_MANUAL) ? + getContext().getString(R.string.download_paused_percent, percent) : + getContext().getString(R.string.download_queued_percent, percent); + } else { + summary = (reason == DownloadManager.PAUSED_BY_MANUAL) ? + getContext().getString(R.string.download_paused) : + getContext().getString(R.string.download_queued); + } break; case DownloadManager.STATUS_PENDING: summary = getContext().getString(R.string.download_queued); break; case DownloadManager.STATUS_RUNNING: - final long progress = cursor.getLong(cursor.getColumnIndexOrThrow( - DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); if (size != null) { String percent = NumberFormat.getPercentInstance().format((double) progress / size); diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java index 325b4eee..c905e25a 100644 --- a/src/com/android/providers/downloads/DownloadThread.java +++ b/src/com/android/providers/downloads/DownloadThread.java @@ -116,6 +116,11 @@ public class DownloadThread implements Runnable { private volatile boolean mPolicyDirty; + // Add for carrier feature - download breakpoint continuing. + // Support continuing download after the download is broken + // although HTTP Server doesn't contain etag in its response. + private final static String QRD_ETAG = "qrd_magic_etag"; + /** * Local changes to {@link DownloadInfo}. These are kept local to avoid * racing with the thread that updates based on change notifications. @@ -686,6 +691,11 @@ public class DownloadThread implements Runnable { if (mInfo.mStatus == Downloads.Impl.STATUS_CANCELED || mInfo.mDeleted) { throw new StopRequestException(Downloads.Impl.STATUS_CANCELED, "download canceled"); } + if (mInfo.mStatus == Downloads.Impl.STATUS_PAUSED_BY_MANUAL) { + // user pauses the download by manual, here send exception and stop data transfer. + throw new StopRequestException(Downloads.Impl.STATUS_PAUSED_BY_MANUAL, + "download paused by manual"); + } } // if policy has been changed, trigger connectivity check @@ -767,6 +777,10 @@ public class DownloadThread implements Runnable { mInfoDelta.mETag = conn.getHeaderField("ETag"); + if (mInfoDelta.mETag == null) { + mInfoDelta.mETag = QRD_ETAG; + } + mInfoDelta.writeToDatabaseOrThrow(); // Check connectivity again now that we know the total size @@ -812,7 +826,9 @@ public class DownloadThread implements Runnable { if (resuming) { if (mInfoDelta.mETag != null) { - conn.addRequestProperty("If-Match", mInfoDelta.mETag); + if (!mInfoDelta.mETag.equals(QRD_ETAG)) { + conn.addRequestProperty("If-Match", mInfoDelta.mETag); + } } conn.addRequestProperty("Range", "bytes=" + mInfoDelta.mCurrentBytes + "-"); } diff --git a/src/com/android/providers/downloads/OpenHelper.java b/src/com/android/providers/downloads/OpenHelper.java index 4eb319c4..8cca9bed 100644 --- a/src/com/android/providers/downloads/OpenHelper.java +++ b/src/com/android/providers/downloads/OpenHelper.java @@ -18,6 +18,7 @@ package com.android.providers.downloads; import static android.app.DownloadManager.COLUMN_LOCAL_FILENAME; import static android.app.DownloadManager.COLUMN_LOCAL_URI; +import static android.app.DownloadManager.COLUMN_MEDIAPROVIDER_URI; import static android.app.DownloadManager.COLUMN_MEDIA_TYPE; import static android.app.DownloadManager.COLUMN_URI; import static android.provider.Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI; @@ -32,6 +33,7 @@ import android.database.Cursor; import android.net.Uri; import android.provider.Downloads.Impl.RequestHeaders; import android.util.Log; +import android.webkit.MimeTypeMap; import java.io.File; @@ -75,6 +77,14 @@ public class OpenHelper { final Uri localUri = getCursorUri(cursor, COLUMN_LOCAL_URI); final File file = getCursorFile(cursor, COLUMN_LOCAL_FILENAME); String mimeType = getCursorString(cursor, COLUMN_MEDIA_TYPE); + if ("application/octet-stream".equals(mimeType)) { + MimeTypeMap m = MimeTypeMap.getSingleton(); + String guess = m.getMimeTypeFromExtension( + m.getFileExtensionFromUrl(localUri.toString())); + if (guess != null) { + mimeType = guess; + } + } mimeType = DownloadDrmHelper.getOriginalMimeType(context, file, mimeType); final Intent intent = new Intent(Intent.ACTION_VIEW); @@ -88,6 +98,10 @@ public class OpenHelper { intent.putExtra(Intent.EXTRA_ORIGINATING_URI, remoteUri); intent.putExtra(Intent.EXTRA_REFERRER, getRefererUri(context, id)); intent.putExtra(Intent.EXTRA_ORIGINATING_UID, getOriginatingUid(context, id)); + } else if (mimeType.startsWith("image/") && !isNull(cursor, COLUMN_MEDIAPROVIDER_URI)) { + final Uri mediaUri = getCursorUri(cursor, COLUMN_MEDIAPROVIDER_URI); + intent.setDataAndType(mediaUri, mimeType); + intent.putExtra("SingleItemOnly", true); } else if ("file".equals(localUri.getScheme())) { intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); @@ -139,6 +153,10 @@ public class OpenHelper { return -1; } + private static Boolean isNull(Cursor cursor, String column) { + return cursor.isNull(cursor.getColumnIndexOrThrow(column)); + } + private static String getCursorString(Cursor cursor, String column) { return cursor.getString(cursor.getColumnIndexOrThrow(column)); } |