* The downloaded file is not scanned by MediaScanner. * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}. *
* By default, downloads are saved to a generated filename in the shared download cache and * may be deleted by the system at any time to reclaim space. * * @return this object */ public Request setDestinationUri(Uri uri) { mDestinationUri = uri; return this; } /** * Set the local destination for the downloaded file to a path within the application's * external files directory (as returned by {@link Context#getExternalFilesDir(String)}. *
* The downloaded file is not scanned by MediaScanner. * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}. * * @param context the {@link Context} to use in determining the external files directory * @param dirType the directory type to pass to {@link Context#getExternalFilesDir(String)} * @param subPath the path within the external directory, including the destination filename * @return this object */ public Request setDestinationInExternalFilesDir(Context context, String dirType, String subPath) { setDestinationFromBase(context.getExternalFilesDir(dirType), subPath); return this; } /** * Set the local destination for the downloaded file to a path within the public external * storage directory (as returned by * {@link Environment#getExternalStoragePublicDirectory(String)}. *
* The downloaded file is not scanned by MediaScanner. * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}. * * @param dirType the directory type to pass to * {@link Environment#getExternalStoragePublicDirectory(String)} * @param subPath the path within the external directory, including the destination filename * @return this object */ public Request setDestinationInExternalPublicDir(String dirType, String subPath) { setDestinationFromBase(Environment.getExternalStoragePublicDirectory(dirType), subPath); return this; } private void setDestinationFromBase(File base, String subPath) { if (subPath == null) { throw new NullPointerException("subPath cannot be null"); } mDestinationUri = Uri.withAppendedPath(Uri.fromFile(base), subPath); } /** * If the file to be downloaded is to be scanned by MediaScanner, this method * should be called before {@link DownloadManager#enqueue(Request)} is called. */ public void allowScanningByMediaScanner() { mScannable = true; } /** * Add an HTTP header to be included with the download request. The header will be added to * the end of the list. * @param header HTTP header name * @param value header value * @return this object * @see HTTP/1.1 * Message Headers */ public Request addRequestHeader(String header, String value) { if (header == null) { throw new NullPointerException("header cannot be null"); } if (header.contains(":")) { throw new IllegalArgumentException("header may not contain ':'"); } if (value == null) { value = ""; } mRequestHeaders.add(Pair.create(header, value)); return this; } /** * Set the title of this download, to be displayed in notifications (if enabled). If no * title is given, a default one will be assigned based on the download filename, once the * download starts. * @return this object */ public Request setTitle(CharSequence title) { mTitle = title; return this; } /** * Set a description of this download, to be displayed in notifications (if enabled) * @return this object */ public Request setDescription(CharSequence description) { mDescription = description; return this; } /** * Set the MIME content type of this download. This will override the content type declared * in the server's response. * @see HTTP/1.1 * Media Types * @return this object */ public Request setMimeType(String mimeType) { mMimeType = mimeType; return this; } /** * Control whether a system notification is posted by the download manager while this * download is running. If enabled, the download manager posts notifications about downloads * through the system {@link android.app.NotificationManager}. By default, a notification is * shown. * * If set to false, this requires the permission * android.permission.DOWNLOAD_WITHOUT_NOTIFICATION. * * @param show whether the download manager should show a notification for this download. * @return this object * @deprecated use {@link #setNotificationVisibility(int)} */ @Deprecated public Request setShowRunningNotification(boolean show) { return (show) ? setNotificationVisibility(VISIBILITY_VISIBLE) : setNotificationVisibility(VISIBILITY_HIDDEN); } /** * Control whether a system notification is posted by the download manager while this * download is running or when it is completed. * If enabled, the download manager posts notifications about downloads * through the system {@link android.app.NotificationManager}. * By default, a notification is shown only when the download is in progress. *
* It can take the following values: {@link #VISIBILITY_HIDDEN}, * {@link #VISIBILITY_VISIBLE}, * {@link #VISIBILITY_VISIBLE_NOTIFY_COMPLETED}. *
* If set to {@link #VISIBILITY_HIDDEN}, this requires the permission
* android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
*
* @param visibility the visibility setting value
* @return this object
*/
public Request setNotificationVisibility(int visibility) {
mNotificationVisibility = visibility;
return this;
}
/**
* Restrict the types of networks over which this download may proceed. By default, all
* network types are allowed.
* @param flags any combination of the NETWORK_* bit flags.
* @return this object
*/
public Request setAllowedNetworkTypes(int flags) {
mAllowedNetworkTypes = flags;
return this;
}
/**
* Set whether this download may proceed over a roaming connection. By default, roaming is
* allowed.
* @param allowed whether to allow a roaming connection to be used
* @return this object
*/
public Request setAllowedOverRoaming(boolean allowed) {
mRoamingAllowed = allowed;
return this;
}
/**
* Set whether this download should be displayed in the system's Downloads UI. True by
* default.
* @param isVisible whether to display this download in the Downloads UI
* @return this object
*/
public Request setVisibleInDownloadsUi(boolean isVisible) {
mIsVisibleInDownloadsUi = isVisible;
return this;
}
/**
* @return ContentValues to be passed to DownloadProvider.insert()
*/
ContentValues toContentValues(String packageName) {
ContentValues values = new ContentValues();
assert mUri != null;
values.put(Downloads.Impl.COLUMN_URI, mUri.toString());
values.put(Downloads.Impl.COLUMN_IS_PUBLIC_API, true);
values.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, packageName);
if (mDestinationUri != null) {
values.put(Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_FILE_URI);
values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT, mDestinationUri.toString());
} else {
values.put(Downloads.Impl.COLUMN_DESTINATION,
Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
}
// is the file supposed to be media-scannable?
values.put(Downloads.Impl.COLUMN_MEDIA_SCANNED, (mScannable) ? SCANNABLE_VALUE_YES :
SCANNABLE_VALUE_NO);
if (!mRequestHeaders.isEmpty()) {
encodeHttpHeaders(values);
}
putIfNonNull(values, Downloads.Impl.COLUMN_TITLE, mTitle);
putIfNonNull(values, Downloads.Impl.COLUMN_DESCRIPTION, mDescription);
putIfNonNull(values, Downloads.Impl.COLUMN_MIME_TYPE, mMimeType);
values.put(Downloads.Impl.COLUMN_VISIBILITY, mNotificationVisibility);
values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes);
values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed);
values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, mIsVisibleInDownloadsUi);
return values;
}
private void encodeHttpHeaders(ContentValues values) {
int index = 0;
for (Pair
* If the specified downloaded file is in external storage (for example, /sdcard dir),
* then it is assumed to be safe for anyone to read and the returned {@link Uri} corresponds
* to the filepath on sdcard.
*
* @param id the id of the downloaded file.
* @return the {@link Uri} for the given downloaded file id, if download was successful. null
* otherwise.
*/
public Uri getUriForDownloadedFile(long id) {
// to check if the file is in cache, get its destination from the database
Query query = new Query().setFilterById(id);
Cursor cursor = null;
try {
cursor = query(query);
if (cursor == null) {
return null;
}
while (cursor.moveToFirst()) {
int status = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_STATUS));
if (DownloadManager.STATUS_SUCCESSFUL == status) {
int indx = cursor.getColumnIndexOrThrow(
Downloads.Impl.COLUMN_DESTINATION);
int destination = cursor.getInt(indx);
// TODO: if we ever add API to DownloadManager to let the caller specify
// non-external storage for a downloaded file, then the following code
// should also check for that destination.
if (destination == Downloads.Impl.DESTINATION_CACHE_PARTITION ||
destination == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING ||
destination == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE) {
// return private uri
return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, id);
} else {
// return public uri
String path = cursor.getString(
cursor.getColumnIndexOrThrow(COLUMN_LOCAL_FILENAME));
return Uri.fromFile(new File(path));
}
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
// downloaded file not found or its status is not 'successfully completed'
return null;
}
/**
* Returns {@link Uri} for the given downloaded file id, if the file is
* downloaded successfully. otherwise, null is returned.
*
* If the specified downloaded file is in external storage (for example, /sdcard dir),
* then it is assumed to be safe for anyone to read and the returned {@link Uri} corresponds
* to the filepath on sdcard.
*
* @param id the id of the downloaded file.
* @return the {@link Uri} for the given downloaded file id, if download was successful. null
* otherwise.
*/
public String getMimeTypeForDownloadedFile(long id) {
Query query = new Query().setFilterById(id);
Cursor cursor = null;
try {
cursor = query(query);
if (cursor == null) {
return null;
}
while (cursor.moveToFirst()) {
return cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_MEDIA_TYPE));
}
} finally {
if (cursor != null) {
cursor.close();
}
}
// downloaded file not found or its status is not 'successfully completed'
return null;
}
/**
* Restart the given downloads, which must have already completed (successfully or not). This
* method will only work when called from within the download manager's process.
* @param ids the IDs of the downloads
* @hide
*/
public void restartDownload(long... ids) {
Cursor cursor = query(new Query().setFilterById(ids));
try {
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
int status = cursor.getInt(cursor.getColumnIndex(COLUMN_STATUS));
if (status != STATUS_SUCCESSFUL && status != STATUS_FAILED) {
throw new IllegalArgumentException("Cannot restart incomplete download: "
+ cursor.getLong(cursor.getColumnIndex(COLUMN_ID)));
}
}
} finally {
cursor.close();
}
ContentValues values = new ContentValues();
values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
values.putNull(Downloads.Impl._DATA);
values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids));
}
/**
* Get the DownloadProvider URI for the download with the given ID.
*/
Uri getDownloadUri(long id) {
return ContentUris.withAppendedId(mBaseUri, id);
}
/**
* Get a parameterized SQL WHERE clause to select a bunch of IDs.
*/
static String getWhereClauseForIds(long[] ids) {
StringBuilder whereClause = new StringBuilder();
whereClause.append("(");
for (int i = 0; i < ids.length; i++) {
if (i > 0) {
whereClause.append("OR ");
}
whereClause.append(Downloads.Impl._ID);
whereClause.append(" = ? ");
}
whereClause.append(")");
return whereClause.toString();
}
/**
* Get the selection args for a clause returned by {@link #getWhereClauseForIds(long[])}.
*/
static String[] getWhereArgsForIds(long[] ids) {
String[] whereArgs = new String[ids.length];
for (int i = 0; i < ids.length; i++) {
whereArgs[i] = Long.toString(ids[i]);
}
return whereArgs;
}
/**
* This class wraps a cursor returned by DownloadProvider -- the "underlying cursor" -- and
* presents a different set of columns, those defined in the DownloadManager.COLUMN_* constants.
* Some columns correspond directly to underlying values while others are computed from
* underlying data.
*/
private static class CursorTranslator extends CursorWrapper {
private Uri mBaseUri;
public CursorTranslator(Cursor cursor, Uri baseUri) {
super(cursor);
mBaseUri = baseUri;
}
@Override
public int getInt(int columnIndex) {
return (int) getLong(columnIndex);
}
@Override
public long getLong(int columnIndex) {
if (getColumnName(columnIndex).equals(COLUMN_REASON)) {
return getReason(super.getInt(getColumnIndex(Downloads.Impl.COLUMN_STATUS)));
} else if (getColumnName(columnIndex).equals(COLUMN_STATUS)) {
return translateStatus(super.getInt(getColumnIndex(Downloads.Impl.COLUMN_STATUS)));
} else {
return super.getLong(columnIndex);
}
}
@Override
public String getString(int columnIndex) {
return (getColumnName(columnIndex).equals(COLUMN_LOCAL_URI)) ? getLocalUri() :
super.getString(columnIndex);
}
private String getLocalUri() {
long destinationType = getLong(getColumnIndex(Downloads.Impl.COLUMN_DESTINATION));
if (destinationType == Downloads.Impl.DESTINATION_FILE_URI ||
destinationType == Downloads.Impl.DESTINATION_EXTERNAL) {
String localPath = getString(getColumnIndex(COLUMN_LOCAL_FILENAME));
if (localPath == null) {
return null;
}
return Uri.fromFile(new File(localPath)).toString();
}
// return content URI for cache download
long downloadId = getLong(getColumnIndex(Downloads.Impl._ID));
return ContentUris.withAppendedId(mBaseUri, downloadId).toString();
}
private long getReason(int status) {
switch (translateStatus(status)) {
case STATUS_FAILED:
return getErrorCode(status);
case STATUS_PAUSED:
return getPausedReason(status);
default:
return 0; // arbitrary value when status is not an error
}
}
private long getPausedReason(int status) {
switch (status) {
case Downloads.Impl.STATUS_WAITING_TO_RETRY:
return PAUSED_WAITING_TO_RETRY;
case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
return PAUSED_WAITING_FOR_NETWORK;
case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
return PAUSED_QUEUED_FOR_WIFI;
default:
return PAUSED_UNKNOWN;
}
}
private long getErrorCode(int status) {
if ((400 <= status && status < Downloads.Impl.MIN_ARTIFICIAL_ERROR_STATUS)
|| (500 <= status && status < 600)) {
// HTTP status code
return status;
}
switch (status) {
case Downloads.Impl.STATUS_FILE_ERROR:
return ERROR_FILE_ERROR;
case Downloads.Impl.STATUS_UNHANDLED_HTTP_CODE:
case Downloads.Impl.STATUS_UNHANDLED_REDIRECT:
return ERROR_UNHANDLED_HTTP_CODE;
case Downloads.Impl.STATUS_HTTP_DATA_ERROR:
return ERROR_HTTP_DATA_ERROR;
case Downloads.Impl.STATUS_TOO_MANY_REDIRECTS:
return ERROR_TOO_MANY_REDIRECTS;
case Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR:
return ERROR_INSUFFICIENT_SPACE;
case Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR:
return ERROR_DEVICE_NOT_FOUND;
case Downloads.Impl.STATUS_CANNOT_RESUME:
return ERROR_CANNOT_RESUME;
case Downloads.Impl.STATUS_FILE_ALREADY_EXISTS_ERROR:
return ERROR_FILE_ALREADY_EXISTS;
default:
return ERROR_UNKNOWN;
}
}
private int translateStatus(int status) {
switch (status) {
case Downloads.Impl.STATUS_PENDING:
return STATUS_PENDING;
case Downloads.Impl.STATUS_RUNNING:
return STATUS_RUNNING;
case Downloads.Impl.STATUS_PAUSED_BY_APP:
case Downloads.Impl.STATUS_WAITING_TO_RETRY:
case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
return STATUS_PAUSED;
case Downloads.Impl.STATUS_SUCCESS:
return STATUS_SUCCESSFUL;
default:
assert Downloads.Impl.isStatusError(status);
return STATUS_FAILED;
}
}
}
}