summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Kondik <shade@chemlab.org>2012-11-18 22:33:11 -0800
committerSteve Kondik <shade@chemlab.org>2012-11-18 22:33:11 -0800
commit516f77f561e1388ae94fe1a26d2a31960074158a (patch)
treea01a9e9deb200f593008b0763f475471502b6728
parent59a409bbc018124a275fbedc9e7d0166e0e18cca (diff)
parent2fa007ef678b2283d47d007aa3dc91af683cc52c (diff)
downloadandroid_packages_providers_DownloadProvider-516f77f561e1388ae94fe1a26d2a31960074158a.tar.gz
android_packages_providers_DownloadProvider-516f77f561e1388ae94fe1a26d2a31960074158a.tar.bz2
android_packages_providers_DownloadProvider-516f77f561e1388ae94fe1a26d2a31960074158a.zip
Merge branch 'jb-mr1-release' of https://android.googlesource.com/platform/packages/providers/DownloadProvider into HEADcm-10.1-M1
-rw-r--r--AndroidManifest.xml2
-rw-r--r--res/values-fa/strings.xml24
-rw-r--r--res/values-in/strings.xml4
-rw-r--r--res/values-ru/strings.xml2
-rw-r--r--res/values-tr/strings.xml2
-rw-r--r--res/values-zh-rCN/strings.xml2
-rw-r--r--src/com/android/providers/downloads/DownloadInfo.java33
-rw-r--r--src/com/android/providers/downloads/DownloadNotification.java15
-rw-r--r--src/com/android/providers/downloads/DownloadReceiver.java28
-rw-r--r--src/com/android/providers/downloads/DownloadService.java22
-rw-r--r--src/com/android/providers/downloads/DownloadThread.java33
-rw-r--r--src/com/android/providers/downloads/OpenHelper.java125
-rw-r--r--src/com/android/providers/downloads/RealSystemFacade.java43
-rw-r--r--src/com/android/providers/downloads/StorageManager.java11
-rw-r--r--src/com/android/providers/downloads/SystemFacade.java32
-rw-r--r--tests/Android.mk2
-rw-r--r--tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java28
-rw-r--r--tests/src/com/android/providers/downloads/FakeSystemFacade.java29
-rw-r--r--tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java56
-rw-r--r--ui/Android.mk5
-rw-r--r--ui/res/mipmap-xxhdpi/ic_launcher_download.pngbin0 -> 21228 bytes
-rw-r--r--ui/res/values-fa/strings.xml6
-rw-r--r--ui/res/values-zh-rCN/strings.xml2
-rw-r--r--ui/src/com/android/providers/downloads/ui/DownloadItem.java2
-rw-r--r--ui/src/com/android/providers/downloads/ui/DownloadList.java20
25 files changed, 336 insertions, 192 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 05df6cd3..7a1ce39e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -58,7 +58,7 @@
<application android:process="android.process.media"
android:label="@string/app_label">
<provider android:name=".DownloadProvider"
- android:authorities="downloads">
+ android:authorities="downloads" android:exported="true">
<!-- Anyone can access /my_downloads, the provider internally restricts access by UID for
these URIs -->
<path-permission android:pathPrefix="/my_downloads"
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 82284299..d8ccb116 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -18,20 +18,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3658948994665187911">"Download Manager"</string>
<string name="permlab_downloadManager" msgid="7779544811202855500">"دسترسی به Download Manager."</string>
- <string name="permdesc_downloadManager" msgid="4237406545998908947">"به برنامه کاربردی اجازه میدهد تا به download manager دسترسی داشته باشد و از آن برای دانلود فایل‌ها استفاده کند. برنامه‌های مضر میتوانند از این امکان استفاده کرده و برای دانلودها مشکل ایجاد کنند و به اطلاعات خصوصی دسترسی داشته باشند."</string>
+ <string name="permdesc_downloadManager" msgid="4237406545998908947">"به برنامهٔ کاربردی اجازه می‌دهد تا به download manager دسترسی داشته باشد و از آن برای دانلود فایل‌ها استفاده کند. برنامه‌های مضر می‌توانند از این امکان استفاده کرده و برای دانلودها مشکل ایجاد کنند و به اطلاعات خصوصی دسترسی داشته باشند."</string>
<string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"عملکرد Download Manager پیشرفته."</string>
- <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"به برنامه کاربردی اجازه میدهد تا به عملکردهای پیشرفته download manager دسترسی داشته باشد. برنامه‌های مضر میتوانند از این امکان استفاده کرده و برای دانلودها مشکل ایجاد کنند و به اطلاعات خصوصی دسترسی داشته باشند."</string>
- <string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"اعلان های دانلود ارسال شود."</string>
- <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"به برنامه کاربردی اجازه میدهد اعلان‌های مربوط به دانلودهای کامل شده را ارسال کند. برنامه‌های مضر میتوانند از این امکان استفاده کرده و برای سایر برنامه‌هایی که فایل‌ها را دانلود میکنند، مشکلاتی را ایجاد کنند."</string>
- <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"مشاهده همه دانلودها در حافظه USB"</string>
- <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"مشاهده همه دانلودها در کارت SD"</string>
- <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"به برنامه کاربردی اجازه میدهد بدون در نظر گرفتن اینکه کدام برنامه دانلود را انجام داده است، تمام دانلودهای موجود در کارت SD را مشاهده کند."</string>
+ <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"به برنامهٔ کاربردی اجازه می‌دهد تا به عملکردهای پیشرفته download manager دسترسی داشته باشد. برنامه‌های مضر می‌توانند از این امکان استفاده کرده و برای دانلودها مشکل ایجاد کنند و به اطلاعات خصوصی دسترسی داشته باشند."</string>
+ <string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"اعلان‌های دانلود ارسال شود."</string>
+ <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"به برنامهٔ کاربردی اجازه می‌دهد اعلان‌های مربوط به دانلودهای کامل شده را ارسال کند. برنامه‌های مضر می‌توانند از این امکان استفاده کرده و برای سایر برنامه‌هایی که فایل‌ها را دانلود می‌کنند، مشکلاتی را ایجاد کنند."</string>
+ <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"مشاهدهٔ همه دانلودها در حافظهٔ USB"</string>
+ <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"مشاهدهٔ همه دانلودها در کارت SD"</string>
+ <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"به برنامهٔ کاربردی اجازه می‌دهد بدون در نظر گرفتن اینکه کدام برنامه دانلود را انجام داده است، تمام دانلودهای موجود در کارت SD را مشاهده کند."</string>
<string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"رزرو فضا در حافظه موقت دانلود"</string>
- <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"به برنامه کاربردی اجازه می‌دهد فایل‌ها را در حافظه پنهان دانلود کند، تا هنگامی که Download Manager به فضای بیشتری احتیاج دارد، بطور خودکار حذف نشوند.+"</string>
- <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"دانلود فایلها بدون اطلاع"</string>
- <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"به برنامه کاربردی اجازه میدهد فایل‌ها را از طریق Download Manager، بدون نمایش اعلان به کاربر دانلود کند."</string>
+ <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"به برنامهٔ کاربردی اجازه می‌دهد فایل‌ها را در حافظهٔ پنهان دانلود کند، تا هنگامی که Download Manager به فضای بیشتری احتیاج دارد، بطور خودکار حذف نشوند.+"</string>
+ <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"دانلود فایل‌ها بدون اطلاع"</string>
+ <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"به برنامهٔ کاربردی اجازه می‌دهد فایل‌ها را از طریق Download Manager، بدون نمایش اعلان به کاربر دانلود کند."</string>
<string name="permlab_accessAllDownloads" msgid="2436240495424393717">"دسترسی به همه دانلودهای سیستم"</string>
- <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"به برنامه کاربردی اجازه میدهد تمام دانلودهای شروع شده توسط هر برنامه‌ای را در سیستم مشاهده کرده و تغییر دهد."</string>
+ <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"به برنامهٔ کاربردی اجازه می‌دهد تمام دانلودهای شروع شده توسط هر برنامه‌ای را در سیستم مشاهده کرده و تغییر دهد."</string>
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;بدون عنوان&gt;"</string>
<string name="notification_filename_separator" msgid="7147189522857807618">"، "</string>
<string name="notification_filename_extras" msgid="5549729917695688191">" و <xliff:g id="NUMBER">%d</xliff:g> بیشتر"</string>
@@ -42,7 +42,7 @@
<string name="wifi_required_title" msgid="1995971416871498179">"دانلود برای شبکه اپراتور بسیار بزرگ است"</string>
<string name="wifi_required_body" msgid="3067694630143784449">"برای تکمیل این دانلود <xliff:g id="SIZE">%s </xliff:g> باید از Wi-Fi استفاده کنید. در اتصال بعدی به شبکه Wi-Fi و برای شروع دانلود، "\n\n"لمس<xliff:g id="QUEUE_TEXT">%s </xliff:g> کنید."</string>
<string name="wifi_recommended_title" msgid="7441589306734687400">"برای دانلود در فرصتی دیگر در صف گذاشته شود؟"</string>
- <string name="wifi_recommended_body" msgid="1314735166699936073">"شروع دانلود <xliff:g id="SIZE">%s </xliff:g> سبب کم شدن طول عمر باتری و/یا استفاده بیش از حد از اتصال داده تلفن همراه میشود که بسته به قرارداد ارائه داده شما ممکن است از طرف اپراتور تلفن هزینه‌هایی برای شما در نظر گرفته شود. "\n\n"<xliff:g id="QUEUE_TEXT">%s</xliff:g> را لمس کنید تا در اتصال بعدی به شبکه Wi-Fi، این دانلود شروع شود."</string>
+ <string name="wifi_recommended_body" msgid="1314735166699936073">"شروع دانلود <xliff:g id="SIZE">%s </xliff:g> سبب کم شدن طول عمر باتری و/یا استفاده بیش از حد از اتصال داده تلفن همراه می‌شود که بسته به قرارداد ارائه داده شما ممکن است از طرف اپراتور تلفن هزینه‌هایی برای شما در نظر گرفته شود. "\n\n"<xliff:g id="QUEUE_TEXT">%s</xliff:g> را لمس کنید تا در اتصال بعدی به شبکه Wi-Fi، این دانلود شروع شود."</string>
<string name="button_queue_for_wifi" msgid="422576726189179221">"صف"</string>
<string name="button_cancel_download" msgid="2430166148737975604">"لغو"</string>
<string name="button_start_now" msgid="792123674007840864">"اکنون شروع"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 29cd3f29..75dad7f1 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -26,8 +26,8 @@
<string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Lihat semua unduhan pada penyimpanan USB"</string>
<string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Melihat semua unduhan pada kartu SD"</string>
<string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Mengizinkan apl melihat semua unduhan ke kartu SD, terlepas apl apa yang mengunduhnya."</string>
- <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Ruang penyimpanan dalam tembolok unduhan"</string>
- <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Mengizinkan apl mengunduh file ke tembolok unduhan yang tidak dapat dihapus secara otomatis saat pengelola unduhan memerlukan lebih banyak ruang."</string>
+ <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Ruang penyimpanan dalam cache unduhan"</string>
+ <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Mengizinkan apl mengunduh file ke cache unduhan yang tidak dapat dihapus secara otomatis saat pengelola unduhan memerlukan lebih banyak ruang."</string>
<string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"unduh file tanpa pemberitahuan"</string>
<string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Mengizinkan apl mengunduh file melalui pengelola unduhan tanpa menampilkan pemberitahuan apa pun kepada pengguna."</string>
<string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Akses semua unduhan sistem"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 58ea71e5..af380780 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -28,7 +28,7 @@
<string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Приложение получит доступ ко всем загрузкам на SD-карте независимо от того, через какое ПО они были загружены."</string>
<string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Резервировать место в кэше загрузки"</string>
<string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Приложение сможет загружать файлы в кэш загрузки (который не может быть автоматически очищен), если менеджеру загрузки потребуется больше места."</string>
- <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"загружать файлы без оповещения"</string>
+ <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"Загрузка файлов без оповещения"</string>
<string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Приложение сможет загружать файлы через диспетчер загрузок без уведомления пользователя."</string>
<string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Доступ ко всем загрузкам"</string>
<string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Приложение сможет открывать и изменять загрузки независимо от того, через какое ПО они были загружены."</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index ece86bd4..66cb2ab3 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -23,7 +23,7 @@
<string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Uygulamaya, indirme yöneticisinin gelişmiş fonksiyonlarına erişme izni verir. Zararlı uygulamalar indirme işlemlerini kesmek ve özel bilgilere erişmek için bunu kullanabilir."</string>
<string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"İndirme bildirimleri gönder."</string>
<string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Uygulamaya, tamamlanan indirme işlemleriyle ilgili bildirim gönderme izni verir. Zararlı uygulamalar dosya indiren diğer uygulamaların çalışma biçimini etkilemek için bunu kullanabilir."</string>
- <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"USB dp brmne yapln tüm indr göst"</string>
+ <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"USB belleğe yapln tüm indr göster"</string>
<string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"SD karta yapılan tüm indirmeleri gör"</string>
<string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Uygulamaya, indiren uygulamadan bağımsız olarak, SD karta yapılan tüm indirme işlemlerini görme izni verir."</string>
<string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"İndirme önbelleğinde alan ayır"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 2197fe79..40681fcc 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="3658948994665187911">"下载管理器"</string>
+ <string name="app_label" msgid="3658948994665187911">"下载管理程序"</string>
<string name="permlab_downloadManager" msgid="7779544811202855500">"访问下载管理器。"</string>
<string name="permdesc_downloadManager" msgid="4237406545998908947">"允许该应用使用下载管理器并通过它下载文件。恶意应用可能会借此中断下载并访问私人信息。"</string>
<string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"高级下载管理器功能。"</string>
diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java
index 28adfbdf..5172b696 100644
--- a/src/com/android/providers/downloads/DownloadInfo.java
+++ b/src/com/android/providers/downloads/DownloadInfo.java
@@ -36,7 +36,6 @@ import android.util.Pair;
import com.android.internal.util.IndentingPrintWriter;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -316,11 +315,8 @@ public class DownloadInfo {
// is the media mounted?
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
case Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR:
- // should check space to make sure it is worth retrying the download.
- // but thats the first thing done by the thread when it retries to download
- // it will fail pretty quickly if there is no space.
- // so, it is not that bad to skip checking space availability here.
- return true;
+ // avoids repetition of retrying download
+ return false;
}
return false;
}
@@ -345,7 +341,7 @@ public class DownloadInfo {
*/
public int checkCanUseNetwork() {
final NetworkInfo info = mSystemFacade.getActiveNetworkInfo(mUid);
- if (info == null) {
+ if (info == null || !info.isConnected()) {
return NETWORK_NO_CONNECTION;
}
if (DetailedState.BLOCKED.equals(info.getDetailedState())) {
@@ -425,6 +421,9 @@ public class DownloadInfo {
case ConnectivityManager.TYPE_WIFI:
return DownloadManager.Request.NETWORK_WIFI;
+ case ConnectivityManager.TYPE_BLUETOOTH:
+ return DownloadManager.Request.NETWORK_BLUETOOTH;
+
default:
return 0;
}
@@ -576,4 +575,24 @@ public class DownloadInfo {
StorageManager.getInstance(mContext));
mSystemFacade.startThread(downloader);
}
+
+ /**
+ * Query and return status of requested download.
+ */
+ public static int queryDownloadStatus(ContentResolver resolver, long id) {
+ final Cursor cursor = resolver.query(
+ ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id),
+ new String[] { Downloads.Impl.COLUMN_STATUS }, null, null, null);
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getInt(0);
+ } else {
+ // TODO: increase strictness of value returned for unknown
+ // downloads; this is safe default for now.
+ return Downloads.Impl.STATUS_PENDING;
+ }
+ } finally {
+ cursor.close();
+ }
+ }
}
diff --git a/src/com/android/providers/downloads/DownloadNotification.java b/src/com/android/providers/downloads/DownloadNotification.java
index bbd39f60..f5778e79 100644
--- a/src/com/android/providers/downloads/DownloadNotification.java
+++ b/src/com/android/providers/downloads/DownloadNotification.java
@@ -17,6 +17,7 @@
package com.android.providers.downloads;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentUris;
import android.content.Context;
@@ -38,9 +39,9 @@ import java.util.HashMap;
*/
class DownloadNotification {
- Context mContext;
- HashMap <String, NotificationItem> mNotifications;
- private SystemFacade mSystemFacade;
+ private Context mContext;
+ private NotificationManager mNotifManager;
+ private HashMap<String, NotificationItem> mNotifications;
/** Time when each {@link DownloadInfo#mId} was first shown. */
private SparseLongArray mFirstShown = new SparseLongArray();
@@ -102,7 +103,8 @@ class DownloadNotification {
*/
DownloadNotification(Context ctx, SystemFacade systemFacade) {
mContext = ctx;
- mSystemFacade = systemFacade;
+ mNotifManager = (NotificationManager) mContext.getSystemService(
+ Context.NOTIFICATION_SERVICE);
mNotifications = new HashMap<String, NotificationItem>();
}
@@ -207,8 +209,7 @@ class DownloadNotification {
builder.setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0));
- mSystemFacade.postNotification(item.mId, builder.getNotification());
-
+ mNotifManager.notify(item.mId, builder.build());
}
}
@@ -262,7 +263,7 @@ class DownloadNotification {
intent.setData(contentUri);
builder.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0));
- mSystemFacade.postNotification(id, builder.getNotification());
+ mNotifManager.notify((int) id, builder.build());
}
private boolean isActiveAndVisible(DownloadInfo download) {
diff --git a/src/com/android/providers/downloads/DownloadReceiver.java b/src/com/android/providers/downloads/DownloadReceiver.java
index 26ad992e..7469508d 100644
--- a/src/com/android/providers/downloads/DownloadReceiver.java
+++ b/src/com/android/providers/downloads/DownloadReceiver.java
@@ -17,6 +17,7 @@
package com.android.providers.downloads;
import android.app.DownloadManager;
+import android.app.NotificationManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
@@ -27,13 +28,12 @@ import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
+import android.provider.BaseColumns;
import android.provider.Downloads;
import android.util.Log;
import com.google.common.annotations.VisibleForTesting;
-import java.io.File;
-
/**
* Receives system broadcasts (boot, network connectivity)
*/
@@ -120,7 +120,9 @@ public class DownloadReceiver extends BroadcastReceiver {
* @param cursor Cursor for reading the download's fields
*/
private void hideNotification(Context context, Uri uri, Cursor cursor) {
- mSystemFacade.cancelNotification(ContentUris.parseId(uri));
+ final NotificationManager notifManager = (NotificationManager) context.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+ notifManager.cancel((int) ContentUris.parseId(uri));
int statusColumn = cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_STATUS);
int status = cursor.getInt(statusColumn);
@@ -141,23 +143,13 @@ public class DownloadReceiver extends BroadcastReceiver {
* has been clicked.
*/
private void openDownload(Context context, Cursor cursor) {
- String filename = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl._DATA));
- String mimetype =
- cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_MIME_TYPE));
- Uri path = Uri.parse(filename);
- // If there is no scheme, then it must be a file
- if (path.getScheme() == null) {
- path = Uri.fromFile(new File(filename));
- }
-
- Intent activityIntent = new Intent(Intent.ACTION_VIEW);
- mimetype = DownloadDrmHelper.getOriginalMimeType(context, filename, mimetype);
- activityIntent.setDataAndType(path, mimetype);
- activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final long id = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID));
+ final Intent intent = OpenHelper.buildViewIntent(context, id);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
- context.startActivity(activityIntent);
+ context.startActivity(intent);
} catch (ActivityNotFoundException ex) {
- Log.d(Constants.TAG, "no activity for " + mimetype, ex);
+ Log.d(Constants.TAG, "no activity for " + intent, ex);
}
}
diff --git a/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java
index 3b566f8e..8380830a 100644
--- a/src/com/android/providers/downloads/DownloadService.java
+++ b/src/com/android/providers/downloads/DownloadService.java
@@ -19,6 +19,7 @@ package com.android.providers.downloads;
import static com.android.providers.downloads.Constants.TAG;
import android.app.AlarmManager;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
@@ -38,7 +39,6 @@ import android.os.RemoteException;
import android.provider.Downloads;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Slog;
import com.android.internal.util.IndentingPrintWriter;
import com.google.android.collect.Maps;
@@ -66,6 +66,7 @@ public class DownloadService extends Service {
/** Class to handle Notification Manager updates */
private DownloadNotification mNotifier;
+ private NotificationManager mNotifManager;
/**
* The Service's view of the list of downloads, mapping download IDs to the corresponding info
@@ -222,7 +223,9 @@ public class DownloadService extends Service {
mMediaScannerConnection = new MediaScannerConnection();
mNotifier = new DownloadNotification(this, mSystemFacade);
- mSystemFacade.cancelAllNotifications();
+ mNotifManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ mNotifManager.cancelAll();
+
mStorageManager = StorageManager.getInstance(getApplicationContext());
updateFromProvider();
}
@@ -465,7 +468,7 @@ public class DownloadService extends Service {
!Downloads.Impl.isStatusCompleted(oldStatus)
&& Downloads.Impl.isStatusCompleted(info.mStatus);
if (lostVisibility || justCompleted) {
- mSystemFacade.cancelNotification(info.mId);
+ mNotifManager.cancel((int) info.mId);
}
info.startIfReady(now, mStorageManager);
@@ -476,17 +479,16 @@ public class DownloadService extends Service {
*/
private void deleteDownloadLocked(long id) {
DownloadInfo info = mDownloads.get(id);
- if (info.shouldScanFile()) {
- scanFile(info, false, false);
- }
if (info.mStatus == Downloads.Impl.STATUS_RUNNING) {
info.mStatus = Downloads.Impl.STATUS_CANCELED;
}
if (info.mDestination != Downloads.Impl.DESTINATION_EXTERNAL && info.mFileName != null) {
- Slog.d(TAG, "deleteDownloadLocked() deleting " + info.mFileName);
+ if (Constants.LOGVV) {
+ Log.d(TAG, "deleteDownloadLocked() deleting " + info.mFileName);
+ }
new File(info.mFileName).delete();
}
- mSystemFacade.cancelNotification(info.mId);
+ mNotifManager.cancel((int) info.mId);
mDownloads.remove(info.mId);
}
@@ -559,7 +561,9 @@ public class DownloadService extends Service {
private void deleteFileIfExists(String path) {
try {
if (!TextUtils.isEmpty(path)) {
- Slog.d(TAG, "deleteFileIfExists() deleting " + path);
+ if (Constants.LOGVV) {
+ Log.d(TAG, "deleteFileIfExists() deleting " + path);
+ }
File file = new File(path);
file.delete();
}
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index bd91eaa1..e74d5c72 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -33,7 +33,6 @@ import android.provider.Downloads;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import android.util.Slog;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
@@ -131,6 +130,21 @@ public class DownloadThread extends Thread {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ try {
+ runInternal();
+ } finally {
+ DownloadHandler.getInstance().dequeueDownload(mInfo.mId);
+ }
+ }
+
+ private void runInternal() {
+ // Skip when download already marked as finished; this download was
+ // probably started again while racing with UpdateThread.
+ if (DownloadInfo.queryDownloadStatus(mContext.getContentResolver(), mInfo.mId)
+ == Downloads.Impl.STATUS_SUCCESS) {
+ Log.d(TAG, "Download " + mInfo.mId + " already finished; skipping");
+ return;
+ }
State state = new State(mInfo);
AndroidHttpClient client = null;
@@ -211,7 +225,6 @@ public class DownloadThread extends Thread {
notifyDownloadCompleted(finalStatus, state.mCountRetry, state.mRetryAfter,
state.mGotData, state.mFilename,
state.mNewUri, state.mMimeType, errorMsg);
- DownloadHandler.getInstance().dequeueDownload(mInfo.mId);
netPolicy.unregisterListener(mPolicyListener);
@@ -330,7 +343,9 @@ public class DownloadThread extends Thread {
closeDestination(state);
if (state.mFilename != null && Downloads.Impl.isStatusError(finalStatus)) {
- Slog.d(TAG, "cleanupDestination() deleting " + state.mFilename);
+ if (Constants.LOGVV) {
+ Log.d(TAG, "cleanupDestination() deleting " + state.mFilename);
+ }
new File(state.mFilename).delete();
state.mFilename = null;
}
@@ -847,8 +862,10 @@ public class DownloadThread extends Thread {
long fileLength = f.length();
if (fileLength == 0) {
// The download hadn't actually started, we can restart from scratch
- Slog.d(TAG, "setupDestinationFile() found fileLength=0, deleting "
- + state.mFilename);
+ if (Constants.LOGVV) {
+ Log.d(TAG, "setupDestinationFile() found fileLength=0, deleting "
+ + state.mFilename);
+ }
f.delete();
state.mFilename = null;
if (Constants.LOGV) {
@@ -857,8 +874,10 @@ public class DownloadThread extends Thread {
}
} else if (mInfo.mETag == null && !mInfo.mNoIntegrity) {
// This should've been caught upon failure
- Slog.d(TAG, "setupDestinationFile() unable to resume download, deleting "
- + state.mFilename);
+ if (Constants.LOGVV) {
+ Log.d(TAG, "setupDestinationFile() unable to resume download, deleting "
+ + state.mFilename);
+ }
f.delete();
throw new StopRequestException(Downloads.Impl.STATUS_CANNOT_RESUME,
"Trying to resume a download that can't be resumed");
diff --git a/src/com/android/providers/downloads/OpenHelper.java b/src/com/android/providers/downloads/OpenHelper.java
new file mode 100644
index 00000000..7eca95c9
--- /dev/null
+++ b/src/com/android/providers/downloads/OpenHelper.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012 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 static android.app.DownloadManager.COLUMN_LOCAL_FILENAME;
+import static android.app.DownloadManager.COLUMN_LOCAL_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;
+
+import android.app.DownloadManager;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Downloads.Impl.RequestHeaders;
+
+public class OpenHelper {
+ /**
+ * Build an {@link Intent} to view the download at current {@link Cursor}
+ * position, handling subtleties around installing packages.
+ */
+ public static Intent buildViewIntent(Context context, long id) {
+ final DownloadManager downManager = (DownloadManager) context.getSystemService(
+ Context.DOWNLOAD_SERVICE);
+ downManager.setAccessAllDownloads(true);
+
+ final Cursor cursor = downManager.query(new DownloadManager.Query().setFilterById(id));
+ try {
+ if (!cursor.moveToFirst()) {
+ throw new IllegalArgumentException("Missing download " + id);
+ }
+
+ final Uri localUri = getCursorUri(cursor, COLUMN_LOCAL_URI);
+ final String filename = getCursorString(cursor, COLUMN_LOCAL_FILENAME);
+ String mimeType = getCursorString(cursor, COLUMN_MEDIA_TYPE);
+ mimeType = DownloadDrmHelper.getOriginalMimeType(context, filename, mimeType);
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ if ("application/vnd.android.package-archive".equals(mimeType)) {
+ // PackageInstaller doesn't like content URIs, so open file
+ intent.setDataAndType(localUri, mimeType);
+
+ // Also splice in details about where it came from
+ final Uri remoteUri = getCursorUri(cursor, COLUMN_URI);
+ 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 ("file".equals(localUri.getScheme())) {
+ intent.setDataAndType(
+ ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id), mimeType);
+ } else {
+ intent.setDataAndType(localUri, mimeType);
+ }
+
+ return intent;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private static Uri getRefererUri(Context context, long id) {
+ final Uri headersUri = Uri.withAppendedPath(
+ ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id),
+ RequestHeaders.URI_SEGMENT);
+ final Cursor headers = context.getContentResolver()
+ .query(headersUri, null, null, null, null);
+ try {
+ while (headers.moveToNext()) {
+ final String header = getCursorString(headers, RequestHeaders.COLUMN_HEADER);
+ if ("Referer".equalsIgnoreCase(header)) {
+ return getCursorUri(headers, RequestHeaders.COLUMN_VALUE);
+ }
+ }
+ } finally {
+ headers.close();
+ }
+ return null;
+ }
+
+ private static int getOriginatingUid(Context context, long id) {
+ final Uri uri = ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id);
+ final Cursor cursor = context.getContentResolver().query(uri, new String[]{Constants.UID},
+ null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getInt(cursor.getColumnIndexOrThrow(Constants.UID));
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ return -1;
+ }
+
+ private static String getCursorString(Cursor cursor, String column) {
+ return cursor.getString(cursor.getColumnIndexOrThrow(column));
+ }
+
+ private static Uri getCursorUri(Cursor cursor, String column) {
+ return Uri.parse(getCursorString(cursor, column));
+ }
+
+ private static long getCursorLong(Cursor cursor, String column) {
+ return cursor.getLong(cursor.getColumnIndexOrThrow(column));
+ }
+}
diff --git a/src/com/android/providers/downloads/RealSystemFacade.java b/src/com/android/providers/downloads/RealSystemFacade.java
index 6580f909..228c7165 100644
--- a/src/com/android/providers/downloads/RealSystemFacade.java
+++ b/src/com/android/providers/downloads/RealSystemFacade.java
@@ -1,26 +1,35 @@
+/*
+ * 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 com.android.providers.downloads;
import android.app.DownloadManager;
-import android.app.Notification;
-import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
import android.telephony.TelephonyManager;
import android.util.Log;
class RealSystemFacade implements SystemFacade {
private Context mContext;
- private NotificationManager mNotificationManager;
public RealSystemFacade(Context context) {
mContext = context;
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
}
public long currentTimeMillis() {
@@ -85,26 +94,6 @@ class RealSystemFacade implements SystemFacade {
}
@Override
- public void postNotification(long id, Notification notification) {
- /**
- * TODO: The system notification manager takes ints, not longs, as IDs, but the download
- * manager uses IDs take straight from the database, which are longs. This will have to be
- * dealt with at some point.
- */
- mNotificationManager.notify((int) id, notification);
- }
-
- @Override
- public void cancelNotification(long id) {
- mNotificationManager.cancel((int) id);
- }
-
- @Override
- public void cancelAllNotifications() {
- mNotificationManager.cancelAll();
- }
-
- @Override
public void startThread(Thread thread) {
thread.start();
}
diff --git a/src/com/android/providers/downloads/StorageManager.java b/src/com/android/providers/downloads/StorageManager.java
index 4b51921f..915d141b 100644
--- a/src/com/android/providers/downloads/StorageManager.java
+++ b/src/com/android/providers/downloads/StorageManager.java
@@ -30,7 +30,6 @@ import android.os.StatFs;
import android.provider.Downloads;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Slog;
import com.android.internal.R;
@@ -342,9 +341,9 @@ class StorageManager {
if (TextUtils.isEmpty(data)) continue;
File file = new File(data);
- if (true || Constants.LOGV) {
- Slog.d(Constants.TAG, "purging " + file.getAbsolutePath() + " for " +
- file.length() + " bytes");
+ if (Constants.LOGV) {
+ Log.d(Constants.TAG, "purging " + file.getAbsolutePath() + " for "
+ + file.length() + " bytes");
}
totalFreed += file.length();
file.delete();
@@ -416,7 +415,9 @@ class StorageManager {
try {
final StructStat stat = Libcore.os.stat(path);
if (stat.st_uid == myUid) {
- Slog.d(TAG, "deleting spurious file " + path);
+ if (Constants.LOGVV) {
+ Log.d(TAG, "deleting spurious file " + path);
+ }
file.delete();
}
} catch (ErrnoException e) {
diff --git a/src/com/android/providers/downloads/SystemFacade.java b/src/com/android/providers/downloads/SystemFacade.java
index d1439354..fda97e08 100644
--- a/src/com/android/providers/downloads/SystemFacade.java
+++ b/src/com/android/providers/downloads/SystemFacade.java
@@ -1,12 +1,25 @@
+/*
+ * 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 com.android.providers.downloads;
-import android.app.Notification;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.NetworkInfo;
-
interface SystemFacade {
/**
* @see System#currentTimeMillis()
@@ -50,21 +63,6 @@ interface SystemFacade {
public boolean userOwnsPackage(int uid, String pckg) throws NameNotFoundException;
/**
- * Post a system notification to the NotificationManager.
- */
- public void postNotification(long id, Notification notification);
-
- /**
- * Cancel a system notification.
- */
- public void cancelNotification(long id);
-
- /**
- * Cancel all system notifications.
- */
- public void cancelAllNotifications();
-
- /**
* Start a thread.
*/
public void startThread(Thread thread);
diff --git a/tests/Android.mk b/tests/Android.mk
index aaf32ba4..ff3e1d47 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -8,7 +8,7 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_INSTRUMENTATION_FOR := DownloadProvider
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := mockwebserver
+LOCAL_STATIC_JAVA_LIBRARIES := mockwebserver littlemock dexmaker
LOCAL_PACKAGE_NAME := DownloadProviderTests
LOCAL_CERTIFICATE := media
diff --git a/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
index 1912e84c..a65693fa 100644
--- a/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
+++ b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
@@ -16,6 +16,9 @@
package com.android.providers.downloads;
+import static com.google.testing.littlemock.LittleMock.mock;
+
+import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -42,9 +45,6 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
public abstract class AbstractDownloadProviderFunctionalTest extends
ServiceTestCase<DownloadService> {
@@ -94,19 +94,14 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
static class TestContext extends RenamingDelegatingContext {
private static final String FILENAME_PREFIX = "test.";
- private Context mRealContext;
- private Set<String> mAllowedSystemServices;
private ContentResolver mResolver;
+ private final NotificationManager mNotifManager;
boolean mHasServiceBeenStarted = false;
public TestContext(Context realContext) {
super(realContext, FILENAME_PREFIX);
- mRealContext = realContext;
- mAllowedSystemServices = new HashSet<String>(Arrays.asList(new String[] {
- Context.NOTIFICATION_SERVICE,
- Context.POWER_SERVICE,
- }));
+ mNotifManager = mock(NotificationManager.class);
}
public void setResolver(ContentResolver resolver) {
@@ -118,7 +113,6 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
*/
@Override
public ContentResolver getContentResolver() {
- assert mResolver != null;
return mResolver;
}
@@ -127,9 +121,10 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
*/
@Override
public Object getSystemService(String name) {
- if (mAllowedSystemServices.contains(name)) {
- return mRealContext.getSystemService(name);
+ if (Context.NOTIFICATION_SERVICE.equals(name)) {
+ return mNotifManager;
}
+
return super.getSystemService(name);
}
@@ -155,10 +150,13 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
protected void setUp() throws Exception {
super.setUp();
- Context realContext = getContext();
+ // Since we're testing a system app, AppDataDirGuesser doesn't find our
+ // cache dir, so set it explicitly.
+ System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
+
+ final Context realContext = getContext();
mTestContext = new TestContext(realContext);
setupProviderAndResolver();
-
mTestContext.setResolver(mResolver);
setContext(mTestContext);
setupService();
diff --git a/tests/src/com/android/providers/downloads/FakeSystemFacade.java b/tests/src/com/android/providers/downloads/FakeSystemFacade.java
index c184de83..6898efdb 100644
--- a/tests/src/com/android/providers/downloads/FakeSystemFacade.java
+++ b/tests/src/com/android/providers/downloads/FakeSystemFacade.java
@@ -1,17 +1,13 @@
package com.android.providers.downloads;
-import android.app.Notification;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
-import android.test.AssertionFailedError;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.Queue;
public class FakeSystemFacade implements SystemFacade {
@@ -22,8 +18,6 @@ public class FakeSystemFacade implements SystemFacade {
Long mMaxBytesOverMobile = null;
Long mRecommendedMaxBytesOverMobile = null;
List<Intent> mBroadcastsSent = new ArrayList<Intent>();
- Map<Long,Notification> mActiveNotifications = new HashMap<Long,Notification>();
- List<Notification> mCanceledNotifications = new ArrayList<Notification>();
Queue<Thread> mStartedThreads = new LinkedList<Thread>();
private boolean returnActualTime = false;
@@ -73,29 +67,6 @@ public class FakeSystemFacade implements SystemFacade {
return true;
}
- @Override
- public void postNotification(long id, Notification notification) {
- if (notification == null) {
- throw new AssertionFailedError("Posting null notification");
- }
- mActiveNotifications.put(id, notification);
- }
-
- @Override
- public void cancelNotification(long id) {
- Notification notification = mActiveNotifications.remove(id);
- if (notification != null) {
- mCanceledNotifications.add(notification);
- }
- }
-
- @Override
- public void cancelAllNotifications() {
- for (long id : mActiveNotifications.keySet()) {
- cancelNotification(id);
- }
- }
-
public boolean startThreadsWithoutWaiting = false;
public void setStartThreadsWithoutWaiting(boolean flag) {
this.startThreadsWithoutWaiting = flag;
diff --git a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
index 2f5282ae..34a69df9 100644
--- a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
+++ b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
@@ -16,7 +16,17 @@
package com.android.providers.downloads;
+import static com.google.testing.littlemock.LittleMock.anyInt;
+import static com.google.testing.littlemock.LittleMock.atLeastOnce;
+import static com.google.testing.littlemock.LittleMock.isA;
+import static com.google.testing.littlemock.LittleMock.never;
+import static com.google.testing.littlemock.LittleMock.times;
+import static com.google.testing.littlemock.LittleMock.verify;
+
import android.app.DownloadManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.ConnectivityManager;
@@ -24,7 +34,6 @@ import android.net.Uri;
import android.os.Environment;
import android.provider.Downloads;
import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.RecordedRequest;
@@ -37,12 +46,14 @@ import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.List;
+
@LargeTest
public class PublicApiFunctionalTest extends AbstractPublicApiTest {
private static final String REDIRECTED_PATH = "/other_path";
private static final String ETAG = "my_etag";
protected File mTestDirectory;
+ private NotificationManager mNotifManager;
public PublicApiFunctionalTest() {
super(new FakeSystemFacade());
@@ -52,6 +63,9 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
protected void setUp() throws Exception {
super.setUp();
+ mNotifManager = (NotificationManager) getContext()
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
mTestDirectory = new File(Environment.getExternalStorageDirectory() + File.separator
+ "download_manager_functional_test");
if (mTestDirectory.exists()) {
@@ -501,24 +515,42 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
assertTrue(mResolver.mNotifyWasCalled);
}
- public void testNotifications() throws Exception {
- enqueueResponse(buildEmptyResponse(HTTP_OK));
+ public void testNotificationNever() throws Exception {
enqueueResponse(buildEmptyResponse(HTTP_OK));
- Download download = enqueueRequest(getRequest().setShowRunningNotification(false));
+ final Download download = enqueueRequest(
+ getRequest().setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN));
download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
- assertEquals(0, mSystemFacade.mActiveNotifications.size());
- assertEquals(0, mSystemFacade.mCanceledNotifications.size());
+ runService();
+
+ verify(mNotifManager, never()).notify(anyInt(), isA(Notification.class));
+ // TODO: verify that it never cancels
+ }
+
+ public void testNotificationVisible() throws Exception {
+ enqueueResponse(buildEmptyResponse(HTTP_OK));
- download = enqueueRequest(getRequest()); // notifications by default
+ // only shows in-progress notifications
+ final Download download = enqueueRequest(getRequest());
download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
- assertEquals(1, mSystemFacade.mActiveNotifications.size());
+ runService();
+
+ // TODO: verify different notif types with tags
+ verify(mNotifManager, atLeastOnce()).notify(anyInt(), isA(Notification.class));
+ verify(mNotifManager, times(1)).cancel(anyInt());
+ }
- // The notification doesn't actually get canceled until the UpdateThread runs again, which
- // gets triggered by the DownloadThread updating the status in the provider.
+ public void testNotificationVisibleComplete() throws Exception {
+ enqueueResponse(buildEmptyResponse(HTTP_OK));
+
+ final Download download = enqueueRequest(getRequest().setNotificationVisibility(
+ DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED));
+ download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
runService();
- assertEquals(0, mSystemFacade.mActiveNotifications.size());
- assertEquals(1, mSystemFacade.mCanceledNotifications.size());
+
+ // TODO: verify different notif types with tags
+ verify(mNotifManager, atLeastOnce()).notify(anyInt(), isA(Notification.class));
+ verify(mNotifManager, times(1)).cancel(anyInt());
}
public void testRetryAfter() throws Exception {
diff --git a/ui/Android.mk b/ui/Android.mk
index 8c925f64..14211ea2 100644
--- a/ui/Android.mk
+++ b/ui/Android.mk
@@ -3,7 +3,10 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ ../src/com/android/providers/downloads/OpenHelper.java \
+ ../src/com/android/providers/downloads/Constants.java \
+ ../src/com/android/providers/downloads/DownloadDrmHelper.java
LOCAL_PACKAGE_NAME := DownloadProviderUi
LOCAL_CERTIFICATE := media
diff --git a/ui/res/mipmap-xxhdpi/ic_launcher_download.png b/ui/res/mipmap-xxhdpi/ic_launcher_download.png
new file mode 100644
index 00000000..0921c124
--- /dev/null
+++ b/ui/res/mipmap-xxhdpi/ic_launcher_download.png
Binary files differ
diff --git a/ui/res/values-fa/strings.xml b/ui/res/values-fa/strings.xml
index 5668de9f..fd81dd26 100644
--- a/ui/res/values-fa/strings.xml
+++ b/ui/res/values-fa/strings.xml
@@ -28,12 +28,12 @@
<string name="download_success" msgid="7006048006543495236">"کامل"</string>
<string name="download_error" msgid="8081329546008568251">"ناموفق"</string>
<string name="dialog_title_not_available" msgid="5746317632356158515">"دانلود ممکن نیست."</string>
- <string name="dialog_failed_body" msgid="587545111677064427">"می خواهید بعداً دوباره برای دانلود فایل تلاش کنید یا آن را از صف خارج کنید؟"</string>
+ <string name="dialog_failed_body" msgid="587545111677064427">"می‌خواهید بعداً دوباره برای دانلود فایل تلاش کنید یا آن را از صف خارج کنید؟"</string>
<string name="dialog_title_queued_body" msgid="6760681913815015219">"فایل‌ در نوبت"</string>
<string name="dialog_queued_body" msgid="708552801635572720">"این فایل برای دانلود در آینده در نوبت قرار گرفته است، بنابراین هنوز در دسترس نیست."</string>
<string name="dialog_file_missing_body" msgid="3223012612774276284">"نمی‌توانم فایل‌های دانلود شده را پیدا کنم."</string>
<string name="dialog_insufficient_space_on_external" msgid="8692452156251449195">"نمی‌توان دانلود را به پایان رساند. فضای کافی در حافظه خارجی وجود ندارد."</string>
- <string name="dialog_insufficient_space_on_cache" msgid="6313630206163908994">"نمی‌توان دانلود را به پایان رساند. فضای کافی در حافظه داخلی دانلود وجود ندارد."</string>
+ <string name="dialog_insufficient_space_on_cache" msgid="6313630206163908994">"نمی‌توان دانلود را به پایان رساند. فضای کافی در حافظهٔ داخلی دانلود وجود ندارد."</string>
<string name="dialog_cannot_resume" msgid="8664509751358983543">"دانلود قطع شد و نمی‌توان آن را از سرگیری کرد."</string>
<string name="dialog_file_already_exists" msgid="8308563940663449590">"دانلود نمی‌شود. فایل مقصد از قبل موجود است."</string>
<string name="dialog_media_not_found" msgid="4468088418758018765">"دانلود نمی‌شود. رسانه خارجی در دسترس نیست."</string>
@@ -46,5 +46,5 @@
<string name="deselect_all" msgid="6348198946254776764">"لغو انتخاب همه"</string>
<string name="select_all" msgid="634074918366265804">"انتخاب همه"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="NUMBER">%1$d</xliff:g> مورد انتخاب شده از <xliff:g id="TOTAL">%2$d</xliff:g> مورد"</string>
- <string name="download_share_dialog" msgid="3355867339806448955">"اشتراک گذاری از طریق"</string>
+ <string name="download_share_dialog" msgid="3355867339806448955">"اشتراک‌گذاری از طریق"</string>
</resources>
diff --git a/ui/res/values-zh-rCN/strings.xml b/ui/res/values-zh-rCN/strings.xml
index d876d02d..ae003361 100644
--- a/ui/res/values-zh-rCN/strings.xml
+++ b/ui/res/values-zh-rCN/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="3070921713463294774">"下载内容"</string>
+ <string name="app_label" msgid="3070921713463294774">"下载"</string>
<string name="download_title_sorted_by_date" msgid="5898014492155434221">"下载内容 - 按日期排序"</string>
<string name="download_title_sorted_by_size" msgid="1417193166677094813">"下载内容 - 按大小排序"</string>
<string name="no_downloads" msgid="1029667411186146836">"无下载内容。"</string>
diff --git a/ui/src/com/android/providers/downloads/ui/DownloadItem.java b/ui/src/com/android/providers/downloads/ui/DownloadItem.java
index 25f58638..e24ac4a4 100644
--- a/ui/src/com/android/providers/downloads/ui/DownloadItem.java
+++ b/ui/src/com/android/providers/downloads/ui/DownloadItem.java
@@ -18,6 +18,7 @@ package com.android.providers.downloads.ui;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
import android.view.MotionEvent;
import android.widget.CheckBox;
import android.widget.Checkable;
@@ -100,6 +101,7 @@ public class DownloadItem extends GridLayout implements Checkable {
case MotionEvent.ACTION_UP:
if (mIsInDownEvent && event.getX() < CHECKMARK_AREA) {
toggle();
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
handled = true;
}
mIsInDownEvent = false;
diff --git a/ui/src/com/android/providers/downloads/ui/DownloadList.java b/ui/src/com/android/providers/downloads/ui/DownloadList.java
index 103d3f83..6ffa0691 100644
--- a/ui/src/com/android/providers/downloads/ui/DownloadList.java
+++ b/ui/src/com/android/providers/downloads/ui/DownloadList.java
@@ -32,6 +32,7 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Parcelable;
+import android.provider.BaseColumns;
import android.provider.Downloads;
import android.util.Log;
import android.util.SparseBooleanArray;
@@ -50,6 +51,8 @@ import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ListView;
import android.widget.Toast;
+import com.android.providers.downloads.OpenHelper;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
@@ -499,7 +502,6 @@ public class DownloadList extends Activity {
* Send an Intent to open the download currently pointed to by the given cursor.
*/
private void openCurrentDownload(Cursor cursor) {
- final long id = cursor.getInt(mIdColumnId);
final Uri localUri = Uri.parse(cursor.getString(mLocalUriColumnId));
try {
getContentResolver().openFileDescriptor(localUri, "r").close();
@@ -512,20 +514,8 @@ public class DownloadList extends Activity {
// close() failed, not a problem
}
- final Uri viewUri;
- final String mimeType = cursor.getString(mMediaTypeColumnId);
- if ("application/vnd.android.package-archive".equals(mimeType)) {
- // PackageInstaller doesn't like content URIs, so open file
- viewUri = localUri;
- } else if ("file".equals(localUri.getScheme())) {
- viewUri = ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
- } else {
- viewUri = localUri;
- }
-
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setDataAndType(viewUri, mimeType);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ final long id = cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID));
+ final Intent intent = OpenHelper.buildViewIntent(this, id);
try {
startActivity(intent);
} catch (ActivityNotFoundException ex) {