From 3c03d1b8b7d96209d7b83b6881421ddfc17ccc52 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 5 Sep 2013 17:13:59 -0700 Subject: 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 --- AndroidManifest.xml | 15 -- res/values/strings.xml | 3 + src/com/android/providers/downloads/Constants.java | 2 +- .../downloads/DownloadStorageProvider.java | 30 +-- .../providers/downloads/TrampolineActivity.java | 37 ---- ui/AndroidManifest.xml | 14 ++ .../providers/downloads/ui/DownloadList.java | 6 +- .../providers/downloads/ui/TrampolineActivity.java | 225 +++++++++++++++++++++ 8 files changed, 263 insertions(+), 69 deletions(-) delete mode 100644 src/com/android/providers/downloads/TrampolineActivity.java create mode 100644 ui/src/com/android/providers/downloads/ui/TrampolineActivity.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 398f8f4c..49f039ef 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -106,20 +106,5 @@ - - - - - - - - - diff --git a/res/values/strings.xml b/res/values/strings.xml index 620e3fad..c5712194 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] --> Unsuccessful + + In progress, %d%% + diff --git a/src/com/android/providers/downloads/Constants.java b/src/com/android/providers/downloads/Constants.java index e33a6362..89210a25 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 04cbf090..f1cd8fa9 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 0f494cff..00000000 --- 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 61669154..93fb5223 100644 --- a/ui/AndroidManifest.xml +++ b/ui/AndroidManifest.xml @@ -25,5 +25,19 @@ + + + + + + + + diff --git a/ui/src/com/android/providers/downloads/ui/DownloadList.java b/ui/src/com/android/providers/downloads/ui/DownloadList.java index 443491a9..991d70b6 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 00000000..e9cc17ef --- /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(); + } + } +} -- cgit v1.2.3