summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-09-06 00:13:59 (GMT)
committerJeff Sharkey <jsharkey@android.com>2013-09-06 15:07:49 (GMT)
commit3c03d1b8b7d96209d7b83b6881421ddfc17ccc52 (patch)
tree4b7361352f28e1d7d20fb2affe773cad5da0a976
parentdd00499d5806ab043e6c882641433a21a9406b6a (diff)
downloadandroid_packages_providers_DownloadProvider-3c03d1b8b7d96209d7b83b6881421ddfc17ccc52.zip
android_packages_providers_DownloadProvider-3c03d1b8b7d96209d7b83b6881421ddfc17ccc52.tar.gz
android_packages_providers_DownloadProvider-3c03d1b8b7d96209d7b83b6881421ddfc17ccc52.tar.bz2
Extend trampoline to show dialogs.
Handle incoming manage requests by launching finished downloads, or showing various retry dialogs. Pipe through summary, show percentage when in progress, and always show total size and MIME type. Bug: 10531347, 10599641 Change-Id: I3be2bc67ea3c0ef795146177200f5be77ad5114e
-rw-r--r--AndroidManifest.xml15
-rw-r--r--res/values/strings.xml3
-rw-r--r--src/com/android/providers/downloads/Constants.java2
-rw-r--r--src/com/android/providers/downloads/DownloadStorageProvider.java30
-rw-r--r--src/com/android/providers/downloads/TrampolineActivity.java37
-rw-r--r--ui/AndroidManifest.xml14
-rw-r--r--ui/src/com/android/providers/downloads/ui/DownloadList.java6
-rw-r--r--ui/src/com/android/providers/downloads/ui/TrampolineActivity.java225
8 files changed, 263 insertions, 69 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 398f8f4..49f039e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -106,20 +106,5 @@
<activity android:name=".SizeLimitActivity"
android:launchMode="singleTask"
android:theme="@style/Theme.Translucent"/>
-
- <!-- PackageInstaller really wants raw files -->
- <activity
- android:name=".TrampolineActivity"
- android:theme="@android:style/Theme.NoDisplay"
- android:permission="android.permission.MANAGE_DOCUMENTS">
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.DEFAULT" />
- <data
- android:scheme="content"
- android:host="com.android.providers.downloads.documents"
- android:mimeType="application/vnd.android.package-archive" />
- </intent-filter>
- </activity>
</application>
</manifest>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 620e3fa..c571219 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -226,4 +226,7 @@
an individual item in the download list. [CHAR LIMIT=24] -->
<string name="download_error">Unsuccessful</string>
+ <!-- Representation of download progress percentage when running. [CHAR LIMIT=24] -->
+ <string name="download_running_percent">In progress, <xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g></string>
+
</resources>
diff --git a/src/com/android/providers/downloads/Constants.java b/src/com/android/providers/downloads/Constants.java
index e33a636..89210a2 100644
--- a/src/com/android/providers/downloads/Constants.java
+++ b/src/com/android/providers/downloads/Constants.java
@@ -176,5 +176,5 @@ public class Constants {
public static final boolean LOGVV = LOCAL_LOGVV && LOGV;
public static final String STORAGE_AUTHORITY = "com.android.providers.downloads.documents";
- public static final String STORAGE_DOC_ID_ROOT = "downloads";
+ public static final String STORAGE_ROOT_ID = "downloads";
}
diff --git a/src/com/android/providers/downloads/DownloadStorageProvider.java b/src/com/android/providers/downloads/DownloadStorageProvider.java
index 04cbf09..f1cd8fa 100644
--- a/src/com/android/providers/downloads/DownloadStorageProvider.java
+++ b/src/com/android/providers/downloads/DownloadStorageProvider.java
@@ -41,7 +41,7 @@ import java.io.FileNotFoundException;
* contents.
*/
public class DownloadStorageProvider extends DocumentsProvider {
- private static final String DOC_ID_ROOT = Constants.STORAGE_DOC_ID_ROOT;
+ private static final String DOC_ID_ROOT = Constants.STORAGE_ROOT_ID;
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_ROOT_TYPE, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
@@ -51,7 +51,8 @@ public class DownloadStorageProvider extends DocumentsProvider {
private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
- Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
+ Document.COLUMN_SUMMARY, Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS,
+ Document.COLUMN_SIZE,
};
private DownloadManager mDm;
@@ -214,33 +215,35 @@ public class DownloadStorageProvider extends DocumentsProvider {
if (mimeType == null) {
mimeType = "application/octet-stream";
}
- Long size = null;
+ Long size = cursor.getLong(
+ cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
+ if (size == -1) {
+ size = null;
+ }
final int status = cursor.getInt(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
switch (status) {
case DownloadManager.STATUS_SUCCESSFUL:
- size = cursor.getLong(
- cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
- if (size == -1) {
- size = null;
- }
break;
case DownloadManager.STATUS_PAUSED:
- mimeType = null;
summary = getContext().getString(R.string.download_queued);
break;
case DownloadManager.STATUS_PENDING:
- mimeType = null;
summary = getContext().getString(R.string.download_queued);
break;
case DownloadManager.STATUS_RUNNING:
- mimeType = null;
- summary = getContext().getString(R.string.download_running);
+ final long progress = cursor.getLong(cursor.getColumnIndexOrThrow(
+ DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
+ if (size != null) {
+ final long percent = progress * 100 / size;
+ summary = getContext().getString(R.string.download_running_percent, percent);
+ } else {
+ summary = getContext().getString(R.string.download_running);
+ }
break;
case DownloadManager.STATUS_FAILED:
default:
- mimeType = null;
summary = getContext().getString(R.string.download_error);
break;
}
@@ -256,6 +259,7 @@ public class DownloadStorageProvider extends DocumentsProvider {
final RowBuilder row = result.newRow();
row.offer(Document.COLUMN_DOCUMENT_ID, docId);
row.offer(Document.COLUMN_DISPLAY_NAME, displayName);
+ row.offer(Document.COLUMN_SUMMARY, summary);
row.offer(Document.COLUMN_SIZE, size);
row.offer(Document.COLUMN_MIME_TYPE, mimeType);
row.offer(Document.COLUMN_LAST_MODIFIED, lastModified);
diff --git a/src/com/android/providers/downloads/TrampolineActivity.java b/src/com/android/providers/downloads/TrampolineActivity.java
deleted file mode 100644
index 0f494cf..0000000
--- a/src/com/android/providers/downloads/TrampolineActivity.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2013 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 com.android.providers.downloads;
-
-import android.app.Activity;
-import android.content.ContentUris;
-import android.content.Intent;
-import android.os.Bundle;
-
-/**
- * PackageInstaller really wants raw files.
- */
-public class TrampolineActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final long id = ContentUris.parseId(getIntent().getData());
- final Intent intent = OpenHelper.buildViewIntent(this, id);
- startActivity(intent);
- finish();
- }
-}
diff --git a/ui/AndroidManifest.xml b/ui/AndroidManifest.xml
index 6166915..93fb522 100644
--- a/ui/AndroidManifest.xml
+++ b/ui/AndroidManifest.xml
@@ -25,5 +25,19 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+
+ <activity
+ android:name=".TrampolineActivity"
+ android:theme="@android:style/Theme.NoDisplay"
+ android:permission="android.permission.MANAGE_DOCUMENTS">
+ <intent-filter>
+ <action android:name="android.provider.action.MANAGE_DOCUMENT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data
+ android:scheme="content"
+ android:host="com.android.providers.downloads.documents"
+ android:mimeType="*/*" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/ui/src/com/android/providers/downloads/ui/DownloadList.java b/ui/src/com/android/providers/downloads/ui/DownloadList.java
index 443491a..991d70b 100644
--- a/ui/src/com/android/providers/downloads/ui/DownloadList.java
+++ b/ui/src/com/android/providers/downloads/ui/DownloadList.java
@@ -151,9 +151,9 @@ public class DownloadList extends Activity {
super.onCreate(icicle);
// Trampoline over to new management UI
- final Intent intent = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENTS);
- intent.setData(DocumentsContract.buildDocumentUri(
- Constants.STORAGE_AUTHORITY, Constants.STORAGE_DOC_ID_ROOT));
+ final Intent intent = new Intent(DocumentsContract.ACTION_MANAGE_ROOT);
+ intent.setData(DocumentsContract.buildRootUri(
+ Constants.STORAGE_AUTHORITY, Constants.STORAGE_ROOT_ID));
startActivity(intent);
finish();
}
diff --git a/ui/src/com/android/providers/downloads/ui/TrampolineActivity.java b/ui/src/com/android/providers/downloads/ui/TrampolineActivity.java
new file mode 100644
index 0000000..e9cc17e
--- /dev/null
+++ b/ui/src/com/android/providers/downloads/ui/TrampolineActivity.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2013 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 com.android.providers.downloads.ui;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.app.FragmentManager;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.providers.downloads.Constants;
+import com.android.providers.downloads.OpenHelper;
+
+import libcore.io.IoUtils;
+
+/**
+ * Intercept all download clicks to provide special behavior. For example,
+ * PackageInstaller really wants raw file paths.
+ */
+public class TrampolineActivity extends Activity {
+ private static final String TAG_PAUSED = "paused";
+ private static final String TAG_FAILED = "failed";
+
+ private static final String KEY_ID = "id";
+ private static final String KEY_REASON = "reason";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final long id = ContentUris.parseId(getIntent().getData());
+
+ final DownloadManager dm = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
+ dm.setAccessAllDownloads(true);
+
+ final int status;
+ final int reason;
+
+ final Cursor cursor = dm.query(new Query().setFilterById(id));
+ try {
+ if (cursor.moveToFirst()) {
+ status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
+ reason = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON));
+ } else {
+ Toast.makeText(this, R.string.dialog_file_missing_body, Toast.LENGTH_SHORT).show();
+ finish();
+ return;
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+
+ Log.d(Constants.TAG, "Found " + id + " with status " + status + ", reason " + reason);
+ switch (status) {
+ case DownloadManager.STATUS_PENDING:
+ case DownloadManager.STATUS_RUNNING:
+ sendRunningDownloadClickedBroadcast(id);
+ finish();
+ break;
+
+ case DownloadManager.STATUS_PAUSED:
+ if (reason == DownloadManager.PAUSED_QUEUED_FOR_WIFI) {
+ PausedDialogFragment.show(getFragmentManager(), id);
+ } else {
+ sendRunningDownloadClickedBroadcast(id);
+ finish();
+ }
+ break;
+
+ case DownloadManager.STATUS_SUCCESSFUL:
+ final Intent intent = OpenHelper.buildViewIntent(this, id);
+ startActivity(intent);
+ finish();
+ break;
+
+ case DownloadManager.STATUS_FAILED:
+ FailedDialogFragment.show(getFragmentManager(), id, reason);
+ break;
+ }
+ }
+
+ private void sendRunningDownloadClickedBroadcast(long id) {
+ final Intent intent = new Intent(Constants.ACTION_LIST);
+ intent.setPackage(Constants.PROVIDER_PACKAGE_NAME);
+ intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, new long[] { id });
+ sendBroadcast(intent);
+ }
+
+ public static class PausedDialogFragment extends DialogFragment {
+ public static void show(FragmentManager fm, long id) {
+ final PausedDialogFragment dialog = new PausedDialogFragment();
+ final Bundle args = new Bundle();
+ args.putLong(KEY_ID, id);
+ dialog.setArguments(args);
+ dialog.show(fm, TAG_PAUSED);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+
+ final DownloadManager dm = (DownloadManager) context.getSystemService(
+ Context.DOWNLOAD_SERVICE);
+ dm.setAccessAllDownloads(true);
+
+ final long id = getArguments().getLong(KEY_ID);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(
+ context, AlertDialog.THEME_HOLO_LIGHT);
+ builder.setTitle(R.string.dialog_title_queued_body);
+ builder.setMessage(R.string.dialog_queued_body);
+
+ builder.setPositiveButton(R.string.keep_queued_download, null);
+
+ builder.setNegativeButton(
+ R.string.remove_download, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dm.remove(id);
+ }
+ });
+
+ return builder.create();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ getActivity().finish();
+ }
+ }
+
+ public static class FailedDialogFragment extends DialogFragment {
+ public static void show(FragmentManager fm, long id, int reason) {
+ final FailedDialogFragment dialog = new FailedDialogFragment();
+ final Bundle args = new Bundle();
+ args.putLong(KEY_ID, id);
+ args.putInt(KEY_REASON, reason);
+ dialog.setArguments(args);
+ dialog.show(fm, TAG_FAILED);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+
+ final DownloadManager dm = (DownloadManager) context.getSystemService(
+ Context.DOWNLOAD_SERVICE);
+ dm.setAccessAllDownloads(true);
+
+ final long id = getArguments().getLong(KEY_ID);
+ final int reason = getArguments().getInt(KEY_REASON);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(
+ context, AlertDialog.THEME_HOLO_LIGHT);
+ builder.setTitle(R.string.dialog_title_not_available);
+
+ final String message;
+ switch (reason) {
+ case DownloadManager.ERROR_FILE_ALREADY_EXISTS:
+ builder.setMessage(R.string.dialog_file_already_exists);
+ break;
+ case DownloadManager.ERROR_INSUFFICIENT_SPACE:
+ builder.setMessage(R.string.dialog_insufficient_space_on_external);
+ break;
+ case DownloadManager.ERROR_DEVICE_NOT_FOUND:
+ builder.setMessage(R.string.dialog_media_not_found);
+ break;
+ case DownloadManager.ERROR_CANNOT_RESUME:
+ builder.setMessage(R.string.dialog_cannot_resume);
+ break;
+ default:
+ builder.setMessage(R.string.dialog_failed_body);
+ }
+
+ builder.setNegativeButton(
+ R.string.delete_download, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dm.remove(id);
+ }
+ });
+
+ builder.setPositiveButton(
+ R.string.retry_download, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dm.restartDownload(id);
+ }
+ });
+
+ return builder.create();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ getActivity().finish();
+ }
+ }
+}