diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2010-02-12 13:48:51 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-02-12 13:48:51 -0800 |
commit | d85639afb62d995856b434635e55523c34b628bb (patch) | |
tree | ecb2399da120dbc21e2692e6b3051ac2e631388b /core/java | |
parent | a16e4ee840db3dc6ecdfe60d11a8036ed4b40e7a (diff) | |
parent | 47f46981cc50f773c445a120da06ae3af866c05f (diff) | |
download | frameworks_base-d85639afb62d995856b434635e55523c34b628bb.zip frameworks_base-d85639afb62d995856b434635e55523c34b628bb.tar.gz frameworks_base-d85639afb62d995856b434635e55523c34b628bb.tar.bz2 |
Merge "Public Download Manager API"
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/net/Downloads.java | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/core/java/android/net/Downloads.java b/core/java/android/net/Downloads.java new file mode 100644 index 0000000..b86a0e2 --- /dev/null +++ b/core/java/android/net/Downloads.java @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.SystemClock; +import android.provider.BaseColumns; +import android.util.Log; + +import java.io.File; + +/** + * The Download Manager + * + * @pending + */ +public final class Downloads { + + public static final class ByUri { + + /** @hide */ + private ByUri() {} + + /** + * Initiate a download where the download will be tracked by its URI. + * @pending + */ + public static boolean startDownloadByUri( + Context context, + String url, + boolean showDownload, + boolean allowRoaming, + String title, + String notification_package, + String notification_class) { + ContentResolver cr = context.getContentResolver(); + + // Tell download manager to start downloading update. + ContentValues values = new ContentValues(); + values.put(android.provider.Downloads.Impl.COLUMN_URI, url); + values.put(android.provider.Downloads.Impl.COLUMN_VISIBILITY, + showDownload ? android.provider.Downloads.Impl.VISIBILITY_VISIBLE + : android.provider.Downloads.Impl.VISIBILITY_HIDDEN); + if (title != null) { + values.put(android.provider.Downloads.Impl.COLUMN_TITLE, title); + } + values.put(android.provider.Downloads.Impl.COLUMN_APP_DATA, url); + values.put(android.provider.Downloads.Impl.COLUMN_DESTINATION, + allowRoaming ? android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION : + android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING); + values.put(android.provider.Downloads.Impl.COLUMN_NO_INTEGRITY, true); // Don't check ETag + if (notification_package != null && notification_class != null) { + values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, notification_package); + values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_CLASS, notification_class); + } + + if (cr.insert(android.provider.Downloads.Impl.CONTENT_URI, values) == null) { + return false; + } + return true; + } + + public static final class StatusInfo { + public boolean completed = false; + /** The filename of the active download. */ + public String filename = null; + /** An opaque id for the download */ + public long id = -1; + /** An opaque status code for the download */ + public int statusCode = -1; + /** Approximate number of bytes downloaded so far, for debugging purposes. */ + public long bytesSoFar = -1; + } + + /** @hide */ + private static final int STATUS_INVALID = 0; + /** @hide */ + private static final int STATUS_DOWNLOADING_UPDATE = 3; + /** @hide */ + private static final int STATUS_DOWNLOADED_UPDATE = 4; + + /** + * Column projection for the query to the download manager. This must match + * with the constants DOWNLOADS_COLUMN_*. + * @hide + */ + private static final String[] DOWNLOADS_PROJECTION = { + BaseColumns._ID, + android.provider.Downloads.Impl.COLUMN_APP_DATA, + android.provider.Downloads.Impl.COLUMN_STATUS, + android.provider.Downloads.Impl._DATA, + android.provider.Downloads.Impl.COLUMN_LAST_MODIFICATION, + android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES, + }; + + /** + * The column index for the ID. + * @hide + */ + private static final int DOWNLOADS_COLUMN_ID = 0; + /** + * The column index for the URI. + * @hide + */ + private static final int DOWNLOADS_COLUMN_URI = 1; + /** + * The column index for the status code. + * @hide + */ + private static final int DOWNLOADS_COLUMN_STATUS = 2; + /** + * The column index for the filename. + * @hide + */ + private static final int DOWNLOADS_COLUMN_FILENAME = 3; + /** + * The column index for the last modification time. + * @hide + */ + private static final int DOWNLOADS_COLUMN_LAST_MODIFICATION = 4; + /** + * The column index for the number of bytes downloaded so far. + * @hide + */ + private static final int DOWNLOADS_COLUMN_CURRENT_BYTES = 5; + + /** + * Gets the status of a download. + * + * @param c A Cursor pointing to a download. The URL column is assumed to be valid. + * @return The status of the download. + * @hide + */ + private static final int getStatusOfDownload( + Cursor c, + long redownload_threshold) { + int status = c.getInt(DOWNLOADS_COLUMN_STATUS); + long realtime = SystemClock.elapsedRealtime(); + + // TODO(dougz): special handling of 503, 404? (eg, special + // explanatory messages to user) + + if (!android.provider.Downloads.Impl.isStatusCompleted(status)) { + // Check if it's stuck + long modified = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION); + long now = System.currentTimeMillis(); + if (now < modified || now - modified > redownload_threshold) { + return STATUS_INVALID; + } + + return STATUS_DOWNLOADING_UPDATE; + } + + if (android.provider.Downloads.Impl.isStatusError(status)) { + return STATUS_INVALID; + } + + String filename = c.getString(DOWNLOADS_COLUMN_FILENAME); + if (filename == null) { + return STATUS_INVALID; + } + + return STATUS_DOWNLOADED_UPDATE; + } + + /** + * Gets a Cursor pointing to the download(s) of the current system update. + * @hide + */ + private static final Cursor getCurrentOtaDownloads(Context context, String url) { + return context.getContentResolver().query( + android.provider.Downloads.Impl.CONTENT_URI, + DOWNLOADS_PROJECTION, + android.provider.Downloads.Impl.COLUMN_APP_DATA + "='" + url.replace("'", "''") + "'", + null, + null); + } + + /** + * Returns a StatusInfo with the result of trying to download the + * given URL. Returns null if no attempts have been made. + * @pending + */ + public static final StatusInfo getStatus( + Context context, + String url, + long redownload_threshold) { + StatusInfo result = null; + boolean hasFailedDownload = false; + long failedDownloadModificationTime = 0; + Cursor c = getCurrentOtaDownloads(context, url); + try { + while (c != null && c.moveToNext()) { + if (result == null) { + result = new StatusInfo(); + } + int status = getStatusOfDownload(c, redownload_threshold); + if (status == STATUS_DOWNLOADING_UPDATE || + status == STATUS_DOWNLOADED_UPDATE) { + result.completed = (status == STATUS_DOWNLOADED_UPDATE); + result.filename = c.getString(DOWNLOADS_COLUMN_FILENAME); + result.id = c.getLong(DOWNLOADS_COLUMN_ID); + result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS); + result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES); + return result; + } + + long modTime = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION); + if (hasFailedDownload && + modTime < failedDownloadModificationTime) { + // older than the one already in result; skip it. + continue; + } + + hasFailedDownload = true; + failedDownloadModificationTime = modTime; + result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS); + result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES); + } + } finally { + c.close(); + } + return result; + } + + /** + * Query where clause for general querying. + * @hide + */ + private static final String QUERY_WHERE_CLAUSE = + android.provider.Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE + "=? AND " + + android.provider.Downloads.Impl.COLUMN_NOTIFICATION_CLASS + "=?"; + + /** + * Delete all the downloads for a package/class pair. + * @pending + */ + public static final void removeAllDownloadsByPackage( + Context context, + String notification_package, + String notification_class) { + context.getContentResolver().delete( + android.provider.Downloads.Impl.CONTENT_URI, + QUERY_WHERE_CLAUSE, + new String[] { notification_package, notification_class }); + } + + /** + * @pending + */ + public static final int getProgressColumnId() { + return 0; + } + + /** + * @pending + */ + public static final int getProgressColumnCurrentBytes() { + return 1; + } + + /** + * @pending + */ + public static final int getProgressColumnTotalBytes() { + return 2; + } + + /** @hide */ + private static final String[] PROJECTION = { + BaseColumns._ID, android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES, android.provider.Downloads.Impl.COLUMN_TOTAL_BYTES + }; + + /** + * @pending + */ + public static final Cursor getProgressCursor(Context context, long id) { + Uri downloadUri = Uri.withAppendedPath(android.provider.Downloads.Impl.CONTENT_URI, String.valueOf(id)); + return context.getContentResolver().query(downloadUri, PROJECTION, null, null, null); + } + } + + /** + * @hide + */ + private Downloads() {} +} |