summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk4
-rw-r--r--AndroidManifest.xml15
-rw-r--r--res/values-af/strings.xml16
-rw-r--r--res/values-b+sr+Latn/strings.xml64
-rw-r--r--res/values-be-rBY/strings.xml66
-rw-r--r--res/values-bn-rBD/strings.xml6
-rw-r--r--res/values-bs-rBA/strings.xml64
-rw-r--r--res/values-eu-rES/strings.xml2
-rw-r--r--res/values-fa/strings.xml2
-rw-r--r--res/values-fr-rCA/strings.xml2
-rw-r--r--res/values-gu-rIN/strings.xml10
-rw-r--r--res/values-kn-rIN/strings.xml6
-rw-r--r--res/values-ky-rKG/strings.xml6
-rw-r--r--res/values-ml-rIN/strings.xml4
-rw-r--r--res/values-my-rMM/strings.xml8
-rw-r--r--res/values-ne-rNP/strings.xml6
-rw-r--r--res/values-pa-rIN/strings.xml10
-rw-r--r--res/values-ro/strings.xml26
-rw-r--r--res/values-ru/strings.xml2
-rw-r--r--res/values-uz-rUZ/strings.xml12
-rw-r--r--src/com/android/providers/downloads/Constants.java6
-rw-r--r--src/com/android/providers/downloads/DownloadIdleService.java25
-rw-r--r--src/com/android/providers/downloads/DownloadInfo.java396
-rw-r--r--src/com/android/providers/downloads/DownloadJobService.java112
-rw-r--r--src/com/android/providers/downloads/DownloadNotifier.java237
-rw-r--r--src/com/android/providers/downloads/DownloadProvider.java133
-rw-r--r--src/com/android/providers/downloads/DownloadReceiver.java117
-rw-r--r--src/com/android/providers/downloads/DownloadScanner.java30
-rw-r--r--src/com/android/providers/downloads/DownloadService.java501
-rw-r--r--src/com/android/providers/downloads/DownloadStorageProvider.java64
-rw-r--r--src/com/android/providers/downloads/DownloadThread.java228
-rw-r--r--src/com/android/providers/downloads/Helpers.java123
-rw-r--r--src/com/android/providers/downloads/OpenHelper.java17
-rw-r--r--src/com/android/providers/downloads/RealSystemFacade.java79
-rw-r--r--src/com/android/providers/downloads/SizeLimitActivity.java137
-rw-r--r--src/com/android/providers/downloads/SystemFacade.java28
-rw-r--r--tests/public_api_access/src/com/android/providers/downloads/public_api_access_tests/PublicApiAccessTest.java2
-rw-r--r--tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java46
-rw-r--r--tests/src/com/android/providers/downloads/AbstractPublicApiTest.java21
-rw-r--r--tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java11
-rw-r--r--tests/src/com/android/providers/downloads/FakeSystemFacade.java76
-rw-r--r--tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java54
-rw-r--r--tests/src/com/android/providers/downloads/StorageTest.java2
-rw-r--r--tests/src/com/android/providers/downloads/ThreadingTest.java17
-rw-r--r--ui/AndroidManifest.xml13
-rw-r--r--ui/res/values-af/strings.xml9
-rw-r--r--ui/res/values-am/strings.xml1
-rw-r--r--ui/res/values-ar/strings.xml1
-rw-r--r--ui/res/values-az-rAZ/strings.xml1
-rw-r--r--ui/res/values-b+sr+Latn/strings.xml51
-rw-r--r--ui/res/values-be-rBY/strings.xml51
-rw-r--r--ui/res/values-bg/strings.xml1
-rw-r--r--ui/res/values-bn-rBD/strings.xml3
-rw-r--r--ui/res/values-bs-rBA/strings.xml51
-rw-r--r--ui/res/values-ca/strings.xml1
-rw-r--r--ui/res/values-cs/strings.xml1
-rw-r--r--ui/res/values-da/strings.xml1
-rw-r--r--ui/res/values-de/strings.xml1
-rw-r--r--ui/res/values-el/strings.xml3
-rw-r--r--ui/res/values-en-rAU/strings.xml1
-rw-r--r--ui/res/values-en-rGB/strings.xml1
-rw-r--r--ui/res/values-en-rIN/strings.xml1
-rw-r--r--ui/res/values-es-rUS/strings.xml1
-rw-r--r--ui/res/values-es/strings.xml1
-rw-r--r--ui/res/values-et-rEE/strings.xml1
-rw-r--r--ui/res/values-eu-rES/strings.xml1
-rw-r--r--ui/res/values-fa/strings.xml1
-rw-r--r--ui/res/values-fi/strings.xml1
-rw-r--r--ui/res/values-fr-rCA/strings.xml1
-rw-r--r--ui/res/values-fr/strings.xml1
-rw-r--r--ui/res/values-gl-rES/strings.xml1
-rw-r--r--ui/res/values-gu-rIN/strings.xml1
-rw-r--r--ui/res/values-hi/strings.xml1
-rw-r--r--ui/res/values-hr/strings.xml1
-rw-r--r--ui/res/values-hu/strings.xml1
-rw-r--r--ui/res/values-hy-rAM/strings.xml3
-rw-r--r--ui/res/values-in/strings.xml1
-rw-r--r--ui/res/values-is-rIS/strings.xml1
-rw-r--r--ui/res/values-it/strings.xml1
-rw-r--r--ui/res/values-iw/strings.xml1
-rw-r--r--ui/res/values-ja/strings.xml1
-rw-r--r--ui/res/values-ka-rGE/strings.xml1
-rw-r--r--ui/res/values-kk-rKZ/strings.xml3
-rw-r--r--ui/res/values-km-rKH/strings.xml1
-rw-r--r--ui/res/values-kn-rIN/strings.xml1
-rw-r--r--ui/res/values-ko/strings.xml1
-rw-r--r--ui/res/values-ky-rKG/strings.xml3
-rw-r--r--ui/res/values-lo-rLA/strings.xml1
-rw-r--r--ui/res/values-lt/strings.xml1
-rw-r--r--ui/res/values-lv/strings.xml1
-rw-r--r--ui/res/values-mk-rMK/strings.xml1
-rw-r--r--ui/res/values-ml-rIN/strings.xml3
-rw-r--r--ui/res/values-mn-rMN/strings.xml1
-rw-r--r--ui/res/values-mr-rIN/strings.xml1
-rw-r--r--ui/res/values-ms-rMY/strings.xml1
-rw-r--r--ui/res/values-my-rMM/strings.xml15
-rw-r--r--ui/res/values-nb/strings.xml1
-rw-r--r--ui/res/values-ne-rNP/strings.xml1
-rw-r--r--ui/res/values-nl/strings.xml1
-rw-r--r--ui/res/values-pa-rIN/strings.xml9
-rw-r--r--ui/res/values-pl/strings.xml1
-rw-r--r--ui/res/values-pt-rPT/strings.xml1
-rw-r--r--ui/res/values-pt/strings.xml1
-rw-r--r--ui/res/values-ro/strings.xml31
-rw-r--r--ui/res/values-ru/strings.xml1
-rw-r--r--ui/res/values-si-rLK/strings.xml1
-rw-r--r--ui/res/values-sk/strings.xml1
-rw-r--r--ui/res/values-sl/strings.xml1
-rw-r--r--ui/res/values-sq-rAL/strings.xml1
-rw-r--r--ui/res/values-sr/strings.xml1
-rw-r--r--ui/res/values-sv/strings.xml1
-rw-r--r--ui/res/values-sw/strings.xml1
-rw-r--r--ui/res/values-ta-rIN/strings.xml1
-rw-r--r--ui/res/values-te-rIN/strings.xml1
-rw-r--r--ui/res/values-th/strings.xml1
-rw-r--r--ui/res/values-tl/strings.xml7
-rw-r--r--ui/res/values-tr/strings.xml1
-rw-r--r--ui/res/values-uk/strings.xml1
-rw-r--r--ui/res/values-ur-rPK/strings.xml1
-rw-r--r--ui/res/values-uz-rUZ/strings.xml15
-rw-r--r--ui/res/values-vi/strings.xml1
-rw-r--r--ui/res/values-zh-rCN/strings.xml1
-rw-r--r--ui/res/values-zh-rHK/strings.xml1
-rw-r--r--ui/res/values-zh-rTW/strings.xml1
-rw-r--r--ui/res/values-zu/strings.xml1
-rw-r--r--ui/res/values/strings.xml4
-rw-r--r--ui/src/com/android/providers/downloads/ui/DownloadList.java38
-rw-r--r--ui/src/com/android/providers/downloads/ui/TrampolineActivity.java40
128 files changed, 1700 insertions, 1510 deletions
diff --git a/Android.mk b/Android.mk
index a2f1e961..d69e42f1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -5,11 +5,11 @@ LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
LOCAL_PACKAGE_NAME := DownloadProvider
LOCAL_CERTIFICATE := media
LOCAL_PRIVILEGED_MODULE := true
-LOCAL_STATIC_JAVA_LIBRARIES := guava
+LOCAL_STATIC_JAVA_LIBRARIES := guava \
+ android-support-documents-archive
include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0ba8eadd..87205238 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -97,19 +97,18 @@
</provider>
<service
- android:name=".DownloadService"
- android:permission="android.permission.ACCESS_DOWNLOAD_MANAGER" />
+ android:name=".DownloadJobService"
+ android:exported="true"
+ android:permission="android.permission.BIND_JOB_SERVICE" />
<service
- android:name="com.android.providers.downloads.DownloadIdleService"
+ android:name=".DownloadIdleService"
android:exported="true"
- android:permission="android.permission.BIND_JOB_SERVICE">
- </service>
+ android:permission="android.permission.BIND_JOB_SERVICE" />
<receiver android:name=".DownloadReceiver" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
- <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.intent.action.UID_REMOVED" />
</intent-filter>
<intent-filter>
@@ -117,9 +116,5 @@
<data android:scheme="file" />
</intent-filter>
</receiver>
-
- <activity android:name=".SizeLimitActivity"
- android:launchMode="singleTask"
- android:theme="@style/Theme.Translucent"/>
</application>
</manifest>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 0daba96d..daeaed91 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -18,20 +18,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3658948994665187911">"Aflaaibestuurder"</string>
<string name="permlab_downloadManager" msgid="7779544811202855500">"Kry toegang tot aflaaibestuurder."</string>
- <string name="permdesc_downloadManager" msgid="4237406545998908947">"Laat die program toe om toegang te kry tot die aflaaibestuurder en dit te gebruik om lêers af te laai. Kwaadwillige programme kan dit gebruik om aflaaisels te ontwrig en toegang tot private inligting te kry."</string>
+ <string name="permdesc_downloadManager" msgid="4237406545998908947">"Laat die program toe om toegang te kry tot die aflaaibestuurder en dit te gebruik om lêers af te laai. Kwaadwillige programme kan dit gebruik om aflaaie te ontwrig en toegang tot private inligting te kry."</string>
<string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Gevorderde aflaaibestuurder-funksies"</string>
- <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Laat die program toe om toegang te kry tot die aflaaibestuurder se gevorderde instellings. Kwaadwillige programme kan dit gebruik om aflaaisels te ontwrig en toegang tot private inligting te kry."</string>
+ <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Laat die program toe om toegang te kry tot die aflaaibestuurder se gevorderde instellings. Kwaadwillige programme kan dit gebruik om aflaaie te ontwrig en toegang tot private inligting te kry."</string>
<string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Stuur aflaaikennisgewings."</string>
- <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Laat die program toe om kennisgewings te stuur oor voltooide aflaaisels. Kwaadwillige programme kan dit gebruik om ander programme wat lêers aflaai, te verwar."</string>
- <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Sien alle aflaaisels na USB-berging"</string>
- <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Sien alle aflaaisels op SD-kaart"</string>
- <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Laat die program toe om alle aflaaisels na die SD-kaart te sien, ongeag deur watter program hulle afgelaai is."</string>
+ <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Laat die program toe om kennisgewings te stuur oor voltooide aflaaie. Kwaadwillige programme kan dit gebruik om ander programme wat lêers aflaai, te verwar."</string>
+ <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Sien alle aflaaie na USB-berging"</string>
+ <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Sien alle aflaaie op SD-kaart"</string>
+ <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Laat die program toe om alle aflaaie na die SD-kaart te sien, ongeag deur watter program hulle afgelaai is."</string>
<string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Reserveer ruimte in die aflaaikas"</string>
<string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Laat die program toe om lêers af te laai na die aflaaikas, wat nie outomaties uitgevee kan word as die aflaaibestuurder meer spasie benodig nie."</string>
<string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"laai lêers af sonder kennisgewing"</string>
<string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Laat die program toe om lêers deur die aflaaibestuurder af te laai sonder enige kennisgewing wat aan die gebruiker gewys word."</string>
<string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Kry toegang tot alle stelselaflaaisels"</string>
- <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Laat die program toe om alle aflaaisels wat deur enige program op hierdie stelsel geïnisieer is, te sien en te wysig."</string>
+ <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Laat die program toe om alle aflaaie wat deur enige program op hierdie stelsel geïnisieer is, te sien en te wysig."</string>
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;Ongetiteld&gt;"</string>
<string name="notification_download_complete" msgid="5443563299253103667">"Aflaai voltooi."</string>
<string name="notification_download_failed" msgid="8612136111952014978">"Aflaai onsuksesvol."</string>
@@ -54,7 +54,7 @@
</plurals>
<string name="download_remaining" msgid="3139295890887972718">"<xliff:g id="DURATION">%s</xliff:g> oor"</string>
<string name="download_no_application_title" msgid="7935659741162801699">"Kan nie lêer oopmaak nie"</string>
- <string name="root_downloads" msgid="4098414876292351487">"Aflaaisels"</string>
+ <string name="root_downloads" msgid="4098414876292351487">"Aflaaie"</string>
<string name="download_queued" msgid="3302638231377947451">"In waglys"</string>
<string name="download_running" msgid="3925050393361158266">"Besig"</string>
<string name="download_error" msgid="5144180777324573236">"Onsuksesvol"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 00000000..75b38d11
--- /dev/null
+++ b/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2007 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.
+ -->
+
+<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">"Menadžer preuzimanja"</string>
+ <string name="permlab_downloadManager" msgid="7779544811202855500">"Pristup menadžeru preuzimanja."</string>
+ <string name="permdesc_downloadManager" msgid="4237406545998908947">"Omogućava aplikaciji da pristupa menadžeru preuzimanja i koristi ga za preuzimanje datoteka. Zlonamerne aplikacije mogu pomoću toga da ometaju preuzimanja i pristupaju privatnim informacijama."</string>
+ <string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Napredne funkcije menadžera preuzimanja."</string>
+ <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Omogućava aplikaciji da pristupa naprednim funkcijama menadžera preuzimanja. Zlonamerne aplikacije mogu pomoću toga da ometaju preuzimanja i pristupaju privatnim informacijama."</string>
+ <string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Šaljite obaveštenja o preuzimanjima."</string>
+ <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Omogućava aplikaciji da šalje obaveštenja o dovršenim preuzimanjima. Zlonamerne aplikacije mogu pomoću toga da zbune ostale aplikacije koje preuzimaju datoteke."</string>
+ <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Pogledajte sva preuzimanja u USB memoriju"</string>
+ <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Sva preuzimanja na SD karticu"</string>
+ <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Omogućava aplikaciji da vidi sve stavke preuzete na SD karticu, bez obzira na to koja aplikacija ih je preuzela."</string>
+ <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Rezerviši prostor u kešu za preuzimanje"</string>
+ <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Omogućava aplikaciji da preuzima datoteke u keš za preuzimanje koji ne može automatski da se izbriše kada je menadžeru preuzimanja potrebno još prostora."</string>
+ <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"preuzmi datoteke bez obaveštenja"</string>
+ <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Omogućava aplikaciji da preuzima datoteke preko menadžera preuzimanja bez prikazivanja ikakvog obaveštenja korisniku."</string>
+ <string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Pristupi svim sistemskim preuzimanjima"</string>
+ <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Omogućava aplikaciji da pregleda i menja sva preuzimanja koja je pokrenula bilo koja aplikacija u sistemu."</string>
+ <string name="download_unknown_title" msgid="7015124071247271585">"&lt;Bez naslova&gt;"</string>
+ <string name="notification_download_complete" msgid="5443563299253103667">"Preuzimanje je završeno."</string>
+ <string name="notification_download_failed" msgid="8612136111952014978">"Preuzimanje nije uspelo."</string>
+ <string name="notification_need_wifi_for_size" msgid="2556172885154833575">"Veličina stavke zahteva Wi-Fi."</string>
+ <string name="notification_paused_in_background" msgid="4328508073283591772">"Pauzirano u pozadini."</string>
+ <string name="wifi_required_title" msgid="1995971416871498179">"Preuzimanje je preveliko za mrežu operatera"</string>
+ <string name="wifi_required_body" msgid="3067694630143784449">"Morate da koristite Wi-Fi da biste dovršili ovo preuzimanje od <xliff:g id="SIZE">%s </xliff:g>. \n\nDodirnite <xliff:g id="QUEUE_TEXT">%s </xliff:g> da biste započeli ovo preuzimanje kada se sledeći put povežete sa Wi-Fi mrežom."</string>
+ <string name="wifi_recommended_title" msgid="7441589306734687400">"Želite li da stavite u red za buduće preuzimanje?"</string>
+ <string name="wifi_recommended_body" msgid="1314735166699936073">"Ovim preuzimanjem od <xliff:g id="SIZE">%s </xliff:g> možete da skratite vek trajanja baterije i/ili preterano koristite mobilnu vezu za prenos podataka, što može da dovede do troškova kod mobilnog operatera shodno tarifnom paketu.\n\n Dodirnite <xliff:g id="QUEUE_TEXT">%s</xliff:g> da biste započeli ovo preuzimanje kada se ponovo povežete sa Wi-Fi mrežom."</string>
+ <string name="button_queue_for_wifi" msgid="422576726189179221">"Red"</string>
+ <string name="button_cancel_download" msgid="2430166148737975604">"Otkaži"</string>
+ <string name="button_start_now" msgid="792123674007840864">"Započni odmah"</string>
+ <plurals name="notif_summary_active" formatted="false" msgid="7290448463204837173">
+ <item quantity="one">Preuzima se <xliff:g id="NUMBER">%d</xliff:g> datoteka</item>
+ <item quantity="few">Preuzimaju se <xliff:g id="NUMBER">%d</xliff:g> datoteke</item>
+ <item quantity="other">Preuzima se <xliff:g id="NUMBER">%d</xliff:g> datoteka</item>
+ </plurals>
+ <plurals name="notif_summary_waiting" formatted="false" msgid="2814217662029273005">
+ <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> datoteka je na čekanju</item>
+ <item quantity="few"><xliff:g id="NUMBER">%d</xliff:g> datoteke su na čekanju</item>
+ <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> datoteka je na čekanju</item>
+ </plurals>
+ <string name="download_remaining" msgid="3139295890887972718">"Još <xliff:g id="DURATION">%s</xliff:g>"</string>
+ <string name="download_no_application_title" msgid="7935659741162801699">"Nije moguće otvoriti datoteku"</string>
+ <string name="root_downloads" msgid="4098414876292351487">"Preuzimanja"</string>
+ <string name="download_queued" msgid="3302638231377947451">"Stavljeno je na čekanje"</string>
+ <string name="download_running" msgid="3925050393361158266">"U toku je"</string>
+ <string name="download_error" msgid="5144180777324573236">"Nije uspelo"</string>
+ <string name="download_running_percent" msgid="4305080769167320204">"U toku, <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+</resources>
diff --git a/res/values-be-rBY/strings.xml b/res/values-be-rBY/strings.xml
new file mode 100644
index 00000000..d2b000d7
--- /dev/null
+++ b/res/values-be-rBY/strings.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2007 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.
+ -->
+
+<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="permlab_downloadManager" msgid="7779544811202855500">"Доступ да менеджэра спампоўванняў."</string>
+ <string name="permdesc_downloadManager" msgid="4237406545998908947">"Дазваляе прыкладанню атрымлiваць доступ да менеджэра спампавання i выкарыстоўваць яго для спампавання файлаў. Шкоднасныя прыкладаннi могуць выкарыстоўваць гэта, каб сарваць загрузку і закрыць доступ да прыватнай інфармацыі."</string>
+ <string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Пашыраныя функцыі менеджэра спампоўванняў."</string>
+ <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Дазваляе прыкладанню атрымлiваць доступ да пашыраных функцый менеджэра спампавання. Шкоднасныя прыкладаннi могуць выкарыстоўваць гэта, каб сарваць загрузку і закрыць доступ да прыватнай інфармацыі."</string>
+ <string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Адпраўляць апавяшчэнні аб спампоўцы."</string>
+ <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Дазваляе прыкладанню адпраўляць апавяшчэннi аб завершаных спампаваннях. Шкоднасныя прыкладаннi могуць выкарыстоўваць гэта, каб негатыўна паўсплываць на іншыя прыкладаннi для спампавання файлаў."</string>
+ <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Прагледзець усе спампоўкі на USB-назапашвальнiк"</string>
+ <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Прагледзець усе спампоўкі на SD-карце"</string>
+ <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Дазваляе прыкладанюе бачыць усе спампоўкі на SD-карце, незалежна ад таго, праз якое прыкладанне яны былi спампаваны."</string>
+ <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Зарэзерваваць месца ў кэшы спампоўкі"</string>
+ <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Дазваляе прыкладанню спампоўваць файлы ў кэш-памяць, якую нельга ачысцiць аўтаматычна, калі менеджэру спампавання спатрэбіцца больш месца на дыске."</string>
+ <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"спампоўваць файлы без апавяшчэння"</string>
+ <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Дазваляе прыкладанню спампоўваць файлы праз менеджэр спампавання, не кажучы пра гэта карыстальнiку."</string>
+ <string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Доступ да ўсіх сістэмных спамповак"</string>
+ <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Дазваляе прыкладанню праглядаць і змяняць усе спампоўкi, пачатыя любым сiстэмным прыкладаннем."</string>
+ <string name="download_unknown_title" msgid="7015124071247271585">"&lt;Без назвы&gt;"</string>
+ <string name="notification_download_complete" msgid="5443563299253103667">"Спампаванне завершана"</string>
+ <string name="notification_download_failed" msgid="8612136111952014978">"Не атрымалася спампаваць."</string>
+ <string name="notification_need_wifi_for_size" msgid="2556172885154833575">"Для спампавання патрабуецца сетка Wi-Fi"</string>
+ <string name="notification_paused_in_background" msgid="4328508073283591772">"Прыпынена ў фон. рэжыме"</string>
+ <string name="wifi_required_title" msgid="1995971416871498179">"Спампоўка занадта вялікая для сеткі аператара"</string>
+ <string name="wifi_required_body" msgid="3067694630143784449">"Каб завяршыць спампаванне файла памерам <xliff:g id="SIZE">%s </xliff:g>, патрабуецца падлучэнне да сеткi Wi-Fi. \n\nДакранiцеся да надпiсу <xliff:g id="QUEUE_TEXT">%s </xliff:g>, каб пачаць спампаванне, калi будзе даступна сетка Wi-Fi."</string>
+ <string name="wifi_recommended_title" msgid="7441589306734687400">"Стаць у чаргу, каб спампаваць пазней?"</string>
+ <string name="wifi_recommended_body" msgid="1314735166699936073">"Спампаванне файла памерам <xliff:g id="SIZE">%s </xliff:g> можа скараціць тэрмін службы акумулятара або прывесці да празмернага выкарыстання мабільнага падлучэння дадзеных, за што вашым мабiльным аператарам можа спаганяцца дадатковая плата.\n\nДакранiцеся да надпiсу <xliff:g id="QUEUE_TEXT">%s</xliff:g>, каб пачаць спампаванне, калi з\'явiцца падлучэнне да сеткi 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>
+ <plurals name="notif_summary_active" formatted="false" msgid="7290448463204837173">
+ <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> файл спампоўваецца</item>
+ <item quantity="few"><xliff:g id="NUMBER">%d</xliff:g> файлы спампоўваюцца</item>
+ <item quantity="many"><xliff:g id="NUMBER">%d</xliff:g> файлаў спампоўваецца</item>
+ <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> файла спампоўваецца</item>
+ </plurals>
+ <plurals name="notif_summary_waiting" formatted="false" msgid="2814217662029273005">
+ <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> файл у рэжыме чакання</item>
+ <item quantity="few"><xliff:g id="NUMBER">%d</xliff:g> файлы ў рэжыме чакання</item>
+ <item quantity="many"><xliff:g id="NUMBER">%d</xliff:g> файлаў у рэжыме чакання</item>
+ <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> файла ў рэжыме чакання</item>
+ </plurals>
+ <string name="download_remaining" msgid="3139295890887972718">"Засталося <xliff:g id="DURATION">%s</xliff:g>"</string>
+ <string name="download_no_application_title" msgid="7935659741162801699">"Не атрымлiваецца адкрыць файл"</string>
+ <string name="root_downloads" msgid="4098414876292351487">"Спампоўкі"</string>
+ <string name="download_queued" msgid="3302638231377947451">"У чарзе"</string>
+ <string name="download_running" msgid="3925050393361158266">"У працэсе"</string>
+ <string name="download_error" msgid="5144180777324573236">"Не атрымалася"</string>
+ <string name="download_running_percent" msgid="4305080769167320204">"У працэсе, <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+</resources>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 57537d4d..90c6ecc4 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -35,12 +35,12 @@
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;শিরোনামহীন&gt;"</string>
<string name="notification_download_complete" msgid="5443563299253103667">"ডাউনলোড সম্পূর্ণ হয়েছে।"</string>
<string name="notification_download_failed" msgid="8612136111952014978">"ডাউনলোড অসফল।"</string>
- <string name="notification_need_wifi_for_size" msgid="2556172885154833575">"ডাউনলোড বড় হওয়ায় Wi-Fi লাগবে।"</string>
+ <string name="notification_need_wifi_for_size" msgid="2556172885154833575">"ডাউনলোড বড় হওয়ায় ওয়াই-ফাই লাগবে।"</string>
<string name="notification_paused_in_background" msgid="4328508073283591772">"পশ্চাদপটে বিরাম দেওয়া আছে।"</string>
<string name="wifi_required_title" msgid="1995971416871498179">"অপারেটর নেটওয়ার্কের জন্য ডাউনলোড অত্যন্ত বড়"</string>
- <string name="wifi_required_body" msgid="3067694630143784449">"এই <xliff:g id="SIZE">%s </xliff:g> ডাউনলোড সম্পূর্ণ করতে আপনাকে অবশ্যই Wi-Fi ব্যবহার করতে হবে।\n\nআপনি পরবর্তীতে যখন Wi-Fi নেটওয়ার্কে সংযুক্ত হবেন তখন এই ডাউনলোড শুরু করতে <xliff:g id="QUEUE_TEXT">%s </xliff:g> স্পর্শ করুন।"</string>
+ <string name="wifi_required_body" msgid="3067694630143784449">"এই <xliff:g id="SIZE">%s </xliff:g> ডাউনলোড সম্পূর্ণ করতে আপনাকে অবশ্যই ওয়াই-ফাই ব্যবহার করতে হবে।\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 আপনার পরবর্তীতে যখন Wi-Fi নেটওয়ার্কে সংযুক্ত হবেন তখন এই ডাউনলোড শুরু করতে <xliff:g id="QUEUE_TEXT">%s</xliff:g> স্পর্শ করুন।"</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> স্পর্শ করুন।"</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-bs-rBA/strings.xml b/res/values-bs-rBA/strings.xml
new file mode 100644
index 00000000..a738b809
--- /dev/null
+++ b/res/values-bs-rBA/strings.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2007 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.
+ -->
+
+<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">"Upravitelj za preuzimanja"</string>
+ <string name="permlab_downloadManager" msgid="7779544811202855500">"Pristupite upravitelju za preuzimanja."</string>
+ <string name="permdesc_downloadManager" msgid="4237406545998908947">"Dopušta aplikacijama da pristupaju upravitelju za preuzimanja i koriste ga za preuzimanje datoteka. Zlonamjerne aplikacije to mogu koristiti da poremete preuzimanja i pristupaju privatnim informacijama."</string>
+ <string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Napredne funkcije upravitelja za preuzimanja."</string>
+ <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Dopušta aplikaciji da pristupa naprednim funkcijama upravitelja za preuzimanja. Zlonamjerne aplikacije to mogu koristiti da poremete preuzimanja i pristupaju privatnim informacijama."</string>
+ <string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Šaljite obavještenja o preuzimanju."</string>
+ <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Dopušta aplikaciji da šalje obavještenja o završenim ažuriranjima. Zlonamjerne aplikacije ovo mogu koristiti da zbune druge aplikacije koje preuzimaju datoteke."</string>
+ <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Prikaži sva preuzimanja na USB pohranu"</string>
+ <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Prikaži sva preuzimanja na SD karticu"</string>
+ <string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Dopušta aplikaciji da vidi sva preuzimanja na SD kartici, bez obzira na to koja ih je aplikacija izvršila."</string>
+ <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Rezerviraj mjesto u keš memoriji preuzimanja"</string>
+ <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Dopušta aplikaciji da preuzme datoteke u keš memoriju preuzimanja, koja se ne može automatski izbrisati kada upravitelju za preuzimanja treba više prostora."</string>
+ <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"preuzimanje datoteka bez obavještenja"</string>
+ <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Dopušta aplikaciji da preuzima datoteke preko upravitelja za preuzimanja bez prikazivanja obavještenja korisniku."</string>
+ <string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Pristup svim preuzimanjima sistema"</string>
+ <string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Dopušta aplikaciji da prikaže i modificira sva preuzimanja koja pokrene bilo koja aplikacija u sistemu."</string>
+ <string name="download_unknown_title" msgid="7015124071247271585">"&lt;Bez naslova&gt;"</string>
+ <string name="notification_download_complete" msgid="5443563299253103667">"Preuzimanje završeno."</string>
+ <string name="notification_download_failed" msgid="8612136111952014978">"Preuzimanje neuspješno."</string>
+ <string name="notification_need_wifi_for_size" msgid="2556172885154833575">"Veličina preuzimanja zahtjeva Wi-Fi."</string>
+ <string name="notification_paused_in_background" msgid="4328508073283591772">"Pauzirano u pozadini."</string>
+ <string name="wifi_required_title" msgid="1995971416871498179">"Preuzimanje je preveliko za mrežu operatera"</string>
+ <string name="wifi_required_body" msgid="3067694630143784449">"Morate koristiti Wi-Fi kako biste dovršili ovo preuzimanje od <xliff:g id="SIZE">%s </xliff:g>. \n\nDodirnite <xliff:g id="QUEUE_TEXT">%s </xliff:g> kako biste pokrenuli ovo preuzimanje kada se narednog puta povežete na Wi-Fi mrežu."</string>
+ <string name="wifi_recommended_title" msgid="7441589306734687400">"Staviti u redoslijed za preuzimanje kasnije?"</string>
+ <string name="wifi_recommended_body" msgid="1314735166699936073">"Pokretanje ovog preuzimanja od <xliff:g id="SIZE">%s </xliff:g> sada može skratiti vijek trajanja baterije i/ili dovesti do prekomjernog korištenja mobilne veze za prenos podataka te se može desiti da vam operater naplati troškove ovisno o pretplati za prenos podataka koju imate.\n\n Dodirnite <xliff:g id="QUEUE_TEXT">%s</xliff:g> kako biste pokrenuli ovo preuzimanje kada se narednog puta povežete na Wi-Fi mrežu."</string>
+ <string name="button_queue_for_wifi" msgid="422576726189179221">"Redoslijed"</string>
+ <string name="button_cancel_download" msgid="2430166148737975604">"Otkaži"</string>
+ <string name="button_start_now" msgid="792123674007840864">"Pokreni sada"</string>
+ <plurals name="notif_summary_active" formatted="false" msgid="7290448463204837173">
+ <item quantity="one">Preuzima se <xliff:g id="NUMBER">%d</xliff:g> datoteka</item>
+ <item quantity="few">Preuzimaju se <xliff:g id="NUMBER">%d</xliff:g> datoteke</item>
+ <item quantity="other">Preuzima se <xliff:g id="NUMBER">%d</xliff:g> datoteka</item>
+ </plurals>
+ <plurals name="notif_summary_waiting" formatted="false" msgid="2814217662029273005">
+ <item quantity="one"><xliff:g id="NUMBER">%d</xliff:g> datoteka na čekanju</item>
+ <item quantity="few"><xliff:g id="NUMBER">%d</xliff:g> datoteke na čekanju</item>
+ <item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> datoteka na čekanju</item>
+ </plurals>
+ <string name="download_remaining" msgid="3139295890887972718">"<xliff:g id="DURATION">%s</xliff:g> lijevo"</string>
+ <string name="download_no_application_title" msgid="7935659741162801699">"Nije moguće otvoriti datoteku"</string>
+ <string name="root_downloads" msgid="4098414876292351487">"Preuzimanja"</string>
+ <string name="download_queued" msgid="3302638231377947451">"U redoslijedu"</string>
+ <string name="download_running" msgid="3925050393361158266">"U toku"</string>
+ <string name="download_error" msgid="5144180777324573236">"Neuspješno"</string>
+ <string name="download_running_percent" msgid="4305080769167320204">"U toku, <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+</resources>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index 282fb2fa..5589bbfa 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -33,7 +33,7 @@
<string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Atzitu sistemaren deskarga guztiak"</string>
<string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Aplikazioek sisteman hasitako deskarga guztiak ikustea eta aldatzea baimentzen die aplikazioei."</string>
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;Izengabea&gt;"</string>
- <string name="notification_download_complete" msgid="5443563299253103667">"Deskarga osatu da."</string>
+ <string name="notification_download_complete" msgid="5443563299253103667">"Osatu da deskarga."</string>
<string name="notification_download_failed" msgid="8612136111952014978">"Ezin izan da deskargatu."</string>
<string name="notification_need_wifi_for_size" msgid="2556172885154833575">"Wi-Fi konexioa behar da."</string>
<string name="notification_paused_in_background" msgid="4328508073283591772">"Atzeko planoan pausatuta"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 473d4d7e..119b2a75 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -27,7 +27,7 @@
<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="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>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 1484556d..768300b3 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -43,7 +43,7 @@
<string name="wifi_recommended_body" msgid="1314735166699936073">"Ce téléchargement (<xliff:g id="SIZE">%s </xliff:g>) risque de décharger votre batterie et/ou d\'entraîner une consommation excessive de votre connexion Internet mobile. Des frais supplémentaires peuvent être facturés par votre opérateur en fonction de votre forfait.\n\nAppuyez sur <xliff:g id="QUEUE_TEXT">%s</xliff:g> pour le lancer lors de votre prochaine connexion au Wi-Fi."</string>
<string name="button_queue_for_wifi" msgid="422576726189179221">"File d\'attente"</string>
<string name="button_cancel_download" msgid="2430166148737975604">"Annuler"</string>
- <string name="button_start_now" msgid="792123674007840864">"Commencer maintenant"</string>
+ <string name="button_start_now" msgid="792123674007840864">"Commencer"</string>
<plurals name="notif_summary_active" formatted="false" msgid="7290448463204837173">
<item quantity="one">Téléchargement de <xliff:g id="NUMBER">%d</xliff:g> fichier</item>
<item quantity="other">Téléchargement de <xliff:g id="NUMBER">%d</xliff:g> fichiers</item>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index 33eb5fc7..2c874147 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -18,20 +18,20 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3658948994665187911">"ડાઉનલોડ સંચાલક"</string>
<string name="permlab_downloadManager" msgid="7779544811202855500">"ડાઉનલોડ સંચાલકને ઍક્સેસ કરો."</string>
- <string name="permdesc_downloadManager" msgid="4237406545998908947">"એપ્લિકેશનને ડાઉનલોડ સંચાલકને ઍક્સેસ કરવાની અને ફાઇલો ડાઉનલોડ કરવા માટે તેના ઉપયોગની મંજૂરી આપે છે. દુભાવનાપૂર્ણ એપ્લિકેશનો આનો ઉપયોગ ડાઉનલોડ્સમાં વિક્ષેપ નાખવા અને ખાનગી માહિતીને ઍક્સેસ કરવા માટે કરી શકે છે."</string>
+ <string name="permdesc_downloadManager" msgid="4237406545998908947">"એપ્લિકેશનને ડાઉનલોડ સંચાલકને ઍક્સેસ કરવાની અને ફાઇલો ડાઉનલોડ કરવા માટે તેના ઉપયોગની મંજૂરી આપે છે. દુભાવનાપૂર્ણ ઍપ્લિકેશનો આનો ઉપયોગ ડાઉનલોડ્સમાં વિક્ષેપ નાખવા અને ખાનગી માહિતીને ઍક્સેસ કરવા માટે કરી શકે છે."</string>
<string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"વિગતવાર ડાઉનલોડ સંચાલક કર્યો."</string>
- <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"એપ્લિકેશનને ડાઉનલોડ સંચાલકના વિગતવાર કાર્યોને ઍક્સેસ કરવાની મંજૂરી આપે છે. દુભાવનાપૂર્ણ એપ્લિકેશનો આનો ઉપયોગ ડાઉનલોડ્સમાં વિક્ષેપ નાખવા અને ખાનગી માહિતીને ઍક્સેસ કરવા માટે કરી શકે છે."</string>
+ <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"એપ્લિકેશનને ડાઉનલોડ સંચાલકના વિગતવાર કાર્યોને ઍક્સેસ કરવાની મંજૂરી આપે છે. દુભાવનાપૂર્ણ ઍપ્લિકેશનો આનો ઉપયોગ ડાઉનલોડ્સમાં વિક્ષેપ નાખવા અને ખાનગી માહિતીને ઍક્સેસ કરવા માટે કરી શકે છે."</string>
<string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"ડાઉનલોડ સૂચનાઓ મોકલો."</string>
- <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"એપ્લિકેશનને પૂર્ણ થયેલ ડાઉનલોડ્સ વિશે સૂચનાઓ મોકલવાની મંજૂરી આપે છે. દુર્ભાવનાપૂર્ણ એપ્લિકેશનો આનો ઉપયોગ ફાઇલો ડાઉનલોડ કરતી અન્ય એપ્લિકેશનોને મૂંઝવણમાં નાખવા માટે કરી શકે છે."</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_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="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"એપ્લિકેશનને વપરાશકર્તાને કોઈપણ સૂચના દર્શાવ્યાં વિના ડાઉનલોડ સંચાલક મારફતે ફાઇલો ડાઉનલોડ કરવાની મંજૂરી આપે છે."</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_download_complete" msgid="5443563299253103667">"ડાઉનલોડ પૂર્ણ થયું."</string>
<string name="notification_download_failed" msgid="8612136111952014978">"ડાઉનલોડ અસફળ થયું."</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 9cebfdcf..5e9acad7 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -35,12 +35,12 @@
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;ಶೀರ್ಷಿಕೆ ರಹಿತ&gt;"</string>
<string name="notification_download_complete" msgid="5443563299253103667">"ಡೌನ್‌ಲೋಡ್‌‌ ಪೂರ್ಣಗೊಂಡಿದೆ."</string>
<string name="notification_download_failed" msgid="8612136111952014978">"ಡೌನ್‌ಲೋಡ್‌ ವಿಫಲಗೊಂಡಿದೆ."</string>
- <string name="notification_need_wifi_for_size" msgid="2556172885154833575">"ಡೌನ್‌ಲೋಡ್‌ ಗಾತ್ರಕ್ಕೆ Wi-Fi ಅಗತ್ಯವಿದೆ."</string>
+ <string name="notification_need_wifi_for_size" msgid="2556172885154833575">"ಡೌನ್‌ಲೋಡ್‌ ಗಾತ್ರಕ್ಕೆ ವೈ-ಫೈ ಅಗತ್ಯವಿದೆ."</string>
<string name="notification_paused_in_background" msgid="4328508073283591772">"ಹಿನ್ನೆಲೆಯಲ್ಲಿ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="wifi_required_title" msgid="1995971416871498179">"ಆಪರೇಟರ್‌ ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಡೌನ್‌ಲೋಡ್ ತುಂಬಾ ದೊಡ್ಡದಾಗಿದೆ"</string>
- <string name="wifi_required_body" msgid="3067694630143784449">"ನೀವು ಈ <xliff:g id="SIZE">%s </xliff:g> ಡೌನ್‌ಲೋಡ್‌ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು ನೀವು Wi-Fi ಅನ್ನು ಬಳಸಬೇಕು. \n\nನೀವು ಸಂಪರ್ಕಿಸಿದ Wi-Fi ನೆಟ್‌ವರ್ಕ್‌ನಿಂದ ಮುಂದಿನ ಬಾರಿ ಈ ಡೌನ್‌ಲೋಡ್‌ ಅನ್ನು ಪ್ರಾರಂಭಿಸಲು <xliff:g id="QUEUE_TEXT">%s </xliff:g> ಸ್ಪರ್ಶಿಸಿ."</string>
+ <string name="wifi_required_body" msgid="3067694630143784449">"ನೀವು ಈ <xliff:g id="SIZE">%s </xliff:g> ಡೌನ್‌ಲೋಡ್‌ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು ನೀವು ವೈ-ಫೈ ಅನ್ನು ಬಳಸಬೇಕು. \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 ನೀವು ಸಂಪರ್ಕಿಸಿದ Wi-Fi ನೆಟ್‌ವರ್ಕ್‌ನಿಂದ ಮುಂದಿನ ಬಾರಿ ಈ ಡೌನ್‌ಲೋಡ್‌ ಪ್ರಾರಂಭಿಸಲು <xliff:g id="QUEUE_TEXT">%s</xliff:g> ಸ್ಪರ್ಶಿಸಿ."</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> ಸ್ಪರ್ಶಿಸಿ."</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-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index e2c3888d..1a7d14d9 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/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>
@@ -33,7 +33,7 @@
<string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Бардык системдик жүктөөлөргө жетүү"</string>
<string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Колдонмого системдеги колдонмолор баштаган бардык жүктөөлөрдү көрүүгө жана өзгөртүүгө уруксат берет."</string>
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;Аталышы жок&gt;"</string>
- <string name="notification_download_complete" msgid="5443563299253103667">"Жүктөө аяктады."</string>
+ <string name="notification_download_complete" msgid="5443563299253103667">"Жүктөлүп алынды."</string>
<string name="notification_download_failed" msgid="8612136111952014978">"Жүктөө ийгиликсиз."</string>
<string name="notification_need_wifi_for_size" msgid="2556172885154833575">"Чоң файл, Wi-Fi керек."</string>
<string name="notification_paused_in_background" msgid="4328508073283591772">"Фондо тыныгууда турат."</string>
@@ -54,7 +54,7 @@
</plurals>
<string name="download_remaining" msgid="3139295890887972718">"<xliff:g id="DURATION">%s</xliff:g> калды"</string>
<string name="download_no_application_title" msgid="7935659741162801699">"Файл ачылбады"</string>
- <string name="root_downloads" msgid="4098414876292351487">"Жүктөөлөр"</string>
+ <string name="root_downloads" msgid="4098414876292351487">"Жүктөлүп алынгандар"</string>
<string name="download_queued" msgid="3302638231377947451">"Кезекте"</string>
<string name="download_running" msgid="3925050393361158266">"Жүктөлүүдө"</string>
<string name="download_error" msgid="5144180777324573236">"Ийгиликсиз"</string>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 27222741..e3578424 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -27,7 +27,7 @@
<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">"ഡൗൺലോഡ് കാഷെയിലേക്ക് ഫയലുകൾ ഡൗൺലോഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു, ഡൗൺലോഡ് മാനേജർക്ക് കൂടുതൽ ഇടം ആവശ്യമായി വരുമ്പോൾ ഇത് യാന്ത്രികമായി ഇല്ലാതാക്കാനാകില്ല."</string>
+ <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"ഡൗൺലോഡ് കാഷെയിലേക്ക് ഫയലുകൾ ഡൗൺലോഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു, ഡൗൺലോഡ് മാനേജർക്ക് കൂടുതൽ ഇടം ആവശ്യമായി വരുമ്പോൾ ഇത് സ്വയമേവ ഇല്ലാതാക്കാനാകില്ല."</string>
<string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"അറിയിപ്പില്ലാതെ ഫയലുകൾ ഡൗൺലോഡുചെയ്യുക"</string>
<string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"ഉപയോക്താവിന് അറിയിപ്പൊന്നും ദൃശ്യമാക്കാതെ തന്നെ ഡൗൺലോഡ് മാനേജർ മുഖേന ഫയലുകൾ ഡൗൺലോഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_accessAllDownloads" msgid="2436240495424393717">"എല്ലാ സിസ്റ്റം ഡൗൺലോഡുകളും ആക്‌സസ്സുചെയ്യുക"</string>
@@ -54,7 +54,7 @@
</plurals>
<string name="download_remaining" msgid="3139295890887972718">"<xliff:g id="DURATION">%s</xliff:g> ശേഷിക്കുന്നു"</string>
<string name="download_no_application_title" msgid="7935659741162801699">"ഫയൽ തുറക്കാനായില്ല"</string>
- <string name="root_downloads" msgid="4098414876292351487">"ഡൗൺലോഡുകൾ"</string>
+ <string name="root_downloads" msgid="4098414876292351487">"ഡൗണ്‍ലോഡ്"</string>
<string name="download_queued" msgid="3302638231377947451">"ക്യൂവിലാണ്"</string>
<string name="download_running" msgid="3925050393361158266">"പുരോഗമിക്കുന്നു"</string>
<string name="download_error" msgid="5144180777324573236">"പരാജയപ്പെട്ടു"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index 71f3651f..209fca4a 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -22,7 +22,7 @@
<string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"အဆင့်မြင့် ဒေါင်းလုပ်မန်နေဂျာ လုပ်ငန်းများ"</string>
<string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"အပလီကေးရှင်းအား ဒေါင်းလုပ်မန်နေဂျာ၏ အဆင့်မြင့် လုပ်ဆောင်ချက်များကို သုံးစွဲခွင့်ပြုမည်။ အန္တရာယ် ရှိသော အပလီကေးရှင်းများက ၎င်းကို အသုံးပြုကာ ဒေါင်းလုပ်ပြုလုပ်ခြင်းကို နှောက်ယှက်ခြင်းနှင့် ကိုယ်ပိုင်အချက်အလက်များကို ဝင်ရောက်ယူနိုင်သည်။"</string>
<string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"ဒေါင်းလုပ်နှင့် ပတ်သက်သော အကြောင်းကြားချက် အားလုံးကိုပို့မည်"</string>
- <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"အပလီကေးရှင်း အား ဒေါင်းလုပ်ပြီးဆုံးပါက အကြောင်းကြားရန် ခွင့်ပြုမည်။ အန္တရာယ် ရှိသော အပလီကေးရှင်းများက ၎င်းကို အသုံးပြုကာ ဖိုင်များကိုဒေါင်းလုပ်ပြုထားသော အပလီကေးရှင်းကို ရောထွေးအောင်ပြုလုပ်နိုင်သည်။"</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>
@@ -38,11 +38,11 @@
<string name="notification_need_wifi_for_size" msgid="2556172885154833575">"ဒေါင်းလုပ်ပမာဏမှာ ဝိုင်ဖိုင်လိုအပ်သည်"</string>
<string name="notification_paused_in_background" msgid="4328508073283591772">"နောက်ခံတွင် ခဏဆိုင်းထားသည်"</string>
<string name="wifi_required_title" msgid="1995971416871498179">"ဒေါင်းလုပ်မှာ အော်ပရေတာကွန်ယက်အတွက် ကြီးလွန်းသည်"</string>
- <string name="wifi_required_body" msgid="3067694630143784449">"ဤ <xliff:g id="SIZE">%s </xliff:g> ဒေါင်းလုပ်ကို ပြုလုပ်ရန် ဝိုင်ဖိုင် ချိတ်ဆက်ထားရပါမည်၊ \n\nနောက်တစ်ခါ ဝိုင်ဖိုင်ကိုချိတ်ဆက်လျှင် ဒေါင်းလုပ်စတင်ရန် <xliff:g id="QUEUE_TEXT">%s </xliff:g> ကို တို့ထိပါ။"</string>
+ <string name="wifi_required_body" msgid="3067694630143784449">"ဤ <xliff:g id="SIZE">%s </xliff:g> ဒေါင်းလုပ်ကို ပြုလုပ်ရန် Wi-Fi ချိတ်ဆက်ထားရပါမည်၊ \n\nနောက်တစ်ခါ Wi-Fi ကိုချိတ်ဆက်လျှင် ဒေါင်းလုပ်စတင်ရန် <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> ကို တို့ထိပါ။"</string>
<string name="button_queue_for_wifi" msgid="422576726189179221">"တန်းစီရန်"</string>
- <string name="button_cancel_download" msgid="2430166148737975604">"ထားတော့"</string>
+ <string name="button_cancel_download" msgid="2430166148737975604">"မလုပ်တော့ပါ"</string>
<string name="button_start_now" msgid="792123674007840864">"ယခုအချိန်မှစတင်ပါ"</string>
<plurals name="notif_summary_active" formatted="false" msgid="7290448463204837173">
<item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> ဖိုင်များကို ဒေါင်းလုဒ် လုပ်နေပါသည်</item>
@@ -54,7 +54,7 @@
</plurals>
<string name="download_remaining" msgid="3139295890887972718">"<xliff:g id="DURATION">%s</xliff:g> ကျန်ရှိသည်"</string>
<string name="download_no_application_title" msgid="7935659741162801699">"ဖိုင်အား ဖွင့်မရပါ"</string>
- <string name="root_downloads" msgid="4098414876292351487">"ဒေါင်းလုပ်များ"</string>
+ <string name="root_downloads" msgid="4098414876292351487">"ဒေါင်းလုဒ်များ"</string>
<string name="download_queued" msgid="3302638231377947451">"တန်းစီထားပြီး"</string>
<string name="download_running" msgid="3925050393361158266">"ပြုလုပ်နေဆဲ"</string>
<string name="download_error" msgid="5144180777324573236">"မအောင်မြင်ပါ"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index 21912b9e..ca95dc8f 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -35,12 +35,12 @@
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;शीर्षकविहिन&gt;"</string>
<string name="notification_download_complete" msgid="5443563299253103667">"डाउनलोड पुरा भयो।"</string>
<string name="notification_download_failed" msgid="8612136111952014978">"डाउनलोड असफल"</string>
- <string name="notification_need_wifi_for_size" msgid="2556172885154833575">"डाउनलोड आकारलाई वाइफाइ चाहिन्छ।"</string>
+ <string name="notification_need_wifi_for_size" msgid="2556172885154833575">"डाउनलोड आकारलाई Wi-Fi चाहिन्छ।"</string>
<string name="notification_paused_in_background" msgid="4328508073283591772">"पृष्ठभूमिमा रोकिएको छ।"</string>
<string name="wifi_required_title" msgid="1995971416871498179">"अपरेटर सञ्जालका लागि डाउनलोड ज्यादै ठूलो"</string>
- <string name="wifi_required_body" msgid="3067694630143784449">"तपाईँले वाइफाइ प्रयोग गर्नु पर्छ यस <xliff:g id="SIZE">%s </xliff:g> डाउनलोड समाप्त गर्न। \n\nछुनुहोस् <xliff:g id="QUEUE_TEXT">%s </xliff:g> यस डाउनलोडलाई सुरु गर्न अर्को पटक तपाईँ वाइफाइ सञ्जालामा जडित भए पछि।"</string>
+ <string name="wifi_required_body" msgid="3067694630143784449">"तपाईँले Wi-Fi प्रयोग गर्नु पर्छ यस <xliff:g id="SIZE">%s </xliff:g> डाउनलोड समाप्त गर्न। \n\nछुनुहोस् <xliff:g id="QUEUE_TEXT">%s </xliff:g> यस डाउनलोडलाई सुरु गर्न अर्को पटक तपाईँ Wi-Fi सञ्जालामा जडित भए पछि।"</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>तल यस डाउनलोडलाई सुरु गर्न अर्को पटक तपाईँ वाइफाइ सन्जालमा जडित हुने बेला।"</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-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 5fa93d4d..2dbe981a 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -26,7 +26,7 @@
<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="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"ਡਾਊਨਲੋਡ ਕੈਚ ਵਿੱਚ ਸਪੇਸ ਰੱਖਿਅਤ ਕਰੋ"</string>
<string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"ਐਪ ਨੂੰ ਡਾਊਨਲੋਡ ਕੈਚ ਵਿੱਚ ਉਹ ਫਾਈਲਾਂ ਡਾਊਨਲੋਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ, ਜੋ ਆਟੋਮੈਟਿਕਲੀ ਨਹੀਂ ਮਿਟਾਈਆਂ ਜਾ ਸਕਦੀਆਂ ਜਦੋਂ ਡਾਊਨਲੋਡ ਪ੍ਰਬੰਧਕ ਨੂੰ ਵੱਧ ਸਪੇਸ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ।"</string>
<string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"ਸੂਚਨਾ ਤੋਂ ਬਿਨਾਂ ਫਾਈਲਾਂ ਡਾਊਨਲੋਡ ਕਰੋ"</string>
<string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"ਐਪ ਨੂੰ ਉਪਭੋਗਤਾ ਨੂੰ ਦਿਖਾਈ ਜਾ ਰਹੀ ਕਿਸੇ ਵੀ ਸੂਚਨਾ ਤੋਂ ਬਿਨਾਂ ਡਾਊਨਲੋਡ ਪ੍ਰਬੰਧਕ ਰਾਹੀਂ ਫਾਈਲਾਂ ਡਾਊਨਲੋਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
@@ -37,10 +37,10 @@
<string name="notification_download_failed" msgid="8612136111952014978">"ਡਾਊਨਲੋਡ ਅਸਫਲ।"</string>
<string name="notification_need_wifi_for_size" msgid="2556172885154833575">"ਡਾਊਨਲੋਡ ਆਕਾਰ ਲਈ Wi-Fi ਦੀ ਲੋੜ ਹੈ।"</string>
<string name="notification_paused_in_background" msgid="4328508073283591772">"ਪਿਛੋਕੜ ਵਿੱਚ ਰੋਕਿਆ।"</string>
- <string name="wifi_required_title" msgid="1995971416871498179">"ਓਪਰੇਟਰ ਨੈਟਵਰਕ ਲਈ ਡਾਊਨਲੋਡ ਬਹੁਤ ਜ਼ਿਆਦਾ ਵੱਡੀ"</string>
- <string name="wifi_required_body" msgid="3067694630143784449">"ਤੁਹਾਨੂੰ ਇਸ <xliff:g id="SIZE">%s </xliff:g> ਡਾਊਨਲੋਡ ਨੂੰ ਪੂਰਾ ਕਰਨ ਲਈ Wi-Fi ਵਰਤਣੀ ਚਾਹੀਦੀ ਹੈ। \n\nਅਗਲੀ ਵਾਰ ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ Wi-Fi ਨੈਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰੋ ਤਾਂ ਇਸ ਡਾਊਨਲੋਡ ਨੂੰ ਚਾਲੂ ਕਰਨ ਲਈ <xliff:g id="QUEUE_TEXT">%s </xliff:g> ਨੂੰ ਛੋਹਵੋ।"</string>
+ <string name="wifi_required_title" msgid="1995971416871498179">"ਓਪਰੇਟਰ ਨੈੱਟਵਰਕ ਲਈ ਡਾਊਨਲੋਡ ਬਹੁਤ ਜ਼ਿਆਦਾ ਵੱਡੀ"</string>
+ <string name="wifi_required_body" msgid="3067694630143784449">"ਤੁਹਾਨੂੰ ਇਸ <xliff:g id="SIZE">%s </xliff:g> ਡਾਊਨਲੋਡ ਨੂੰ ਪੂਰਾ ਕਰਨ ਲਈ Wi-Fi ਵਰਤਣੀ ਚਾਹੀਦੀ ਹੈ। \n\nਅਗਲੀ ਵਾਰ ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ Wi-Fi ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰੋ ਤਾਂ ਇਸ ਡਾਊਨਲੋਡ ਨੂੰ ਚਾਲੂ ਕਰਨ ਲਈ <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 ਅਗਲੀ ਵਾਰ ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ Wi-Fi ਨੈਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰੋ ਤਾਂ ਇਸ ਡਾਊਨਲੋਡ ਨੂੰ ਚਾਲੂ ਕਰਨ ਲਈ <xliff:g id="QUEUE_TEXT">%s</xliff:g> ਨੂੰ ਛੋਹਵੋ।"</string>
+ <string name="wifi_recommended_body" msgid="1314735166699936073">"ਹੁਣ ਇਸ <xliff:g id="SIZE">%s </xliff:g> ਡਾਊਨਲੋਡ ਨੂੰ ਚਾਲੂ ਕਰਨ ਨਾਲ ਤੁਹਾਡੀ ਬੈਟਰੀ ਸਮਰੱਥ ਘਟ ਸਕਦੀ ਹੈ ਅਤੇ/ਜਾਂ ਇਸਦੇ ਸਿੱਟੇ ਵਜੋਂ ਤੁਹਾਡੇ ਮੋਬਾਈਲ ਡੈਟਾ ਕਨੈਕਸ਼ਨ ਦੀ ਵਾਧੂ ਵਰਤੋਂ ਹੋ ਸਕਦੀ ਹੈ, ਜਿਸ ਨਾਲ ਤੁਹਾਡੇ ਡੈਟਾ ਪਲਾਨ ਦੇ ਆਧਾਰ ਤੇ ਤੁਹਾਡੇ ਮੋਬਾਈਲ ਓਪਰੇਟਰ ਵੱਲੋਂ ਖ਼ਰਚੇ ਪਾਏ ਜਾ ਸਕਦੇ ਹਨ।\n\n ਅਗਲੀ ਵਾਰ ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ Wi-Fi ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰੋ ਤਾਂ ਇਸ ਡਾਊਨਲੋਡ ਨੂੰ ਚਾਲੂ ਕਰਨ ਲਈ <xliff:g id="QUEUE_TEXT">%s</xliff:g> ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ।"</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>
@@ -54,7 +54,7 @@
</plurals>
<string name="download_remaining" msgid="3139295890887972718">"<xliff:g id="DURATION">%s</xliff:g> ਛੱਡਿਆ"</string>
<string name="download_no_application_title" msgid="7935659741162801699">"ਫਾਈਲ ਨਹੀਂ ਖੋਲ੍ਹ ਸਕਦਾ"</string>
- <string name="root_downloads" msgid="4098414876292351487">"ਡਾਊਨਲੋਡਸ"</string>
+ <string name="root_downloads" msgid="4098414876292351487">"ਡਾਊਨਲੋਡ"</string>
<string name="download_queued" msgid="3302638231377947451">"ਕਤਾਰਬੱਧ"</string>
<string name="download_running" msgid="3925050393361158266">"ਪ੍ਰਗਤੀ ਵਿੱਚ"</string>
<string name="download_error" msgid="5144180777324573236">"ਅਸਫਲ"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 77e5a35a..895653ec 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -18,32 +18,32 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3658948994665187911">"Managerul de descărcare"</string>
<string name="permlab_downloadManager" msgid="7779544811202855500">"Accesați managerul de descărcare."</string>
- <string name="permdesc_downloadManager" msgid="4237406545998908947">"Permite aplicației să acceseze managerul de descărcări și să îl utilizeze pentru a descărca fișiere. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a perturba descărcările și pentru a accesa informații private."</string>
+ <string name="permdesc_downloadManager" msgid="4237406545998908947">"Permite aplicației să acceseze managerul de descărcări și să îl utilizeze pentru a descărca fișiere. Aplicațiile rău intenționate pot utiliza această permisiune pentru a perturba descărcările și pentru a accesa informații private."</string>
<string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Funcții avansate ale managerului de descărcare."</string>
- <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Permite aplicației să acceseze funcţiile avansate ale managerului de descărcări. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a perturba descărcările și pentru a accesa informații private."</string>
+ <string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Permite aplicației să acceseze funcțiile avansate ale managerului de descărcări. Aplicațiile rău intenționate pot utiliza această permisiune pentru a perturba descărcările și pentru a accesa informații private."</string>
<string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Trimiteți notificările descărcării."</string>
- <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Permite aplicației să trimită notificări despre descărcările finalizate. Aplicaţiile malware pot folosi această permisiune pentru a deruta alte aplicații care descarcă fişiere."</string>
+ <string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Permite aplicației să trimită notificări despre descărcările finalizate. Aplicațiile malware pot folosi această permisiune pentru a deruta alte aplicații care descarcă fișiere."</string>
<string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"Vedeți desc. în stoc. USB"</string>
<string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"Vizualizați toate descărcările pe cardul SD"</string>
<string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Permite aplicației să vizualizeze toate descărcările efectuate pe cardul SD, indiferent de aplicația care le-a descărcat."</string>
- <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Rezervați spaţiu în memoria cache de descărcare"</string>
- <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Permite aplic. să descarce fişiere în memoria cache de desc., care nu poate fi ştearsă în mod automat când managerul de desc. are nevoie de mai mult spaţiu."</string>
- <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"descărcarea fişierelor fără notificare"</string>
- <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Permite aplicației să descarce fişiere prin intermediul managerului de descărcări fără a afişa notificări utilizatorului."</string>
+ <string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Rezervați spațiu în memoria cache de descărcare"</string>
+ <string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Permite aplic. să descarce fișiere în memoria cache de desc., care nu poate fi ștearsă în mod automat când managerul de desc. are nevoie de mai mult spațiu."</string>
+ <string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"descărcarea fișierelor fără notificare"</string>
+ <string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Permite aplicației să descarce fișiere prin intermediul managerului de descărcări fără a afișa notificări utilizatorului."</string>
<string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Accesați toate descărcările din sistem"</string>
<string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Permite aplicației să vizualizeze și să modifice toate descărcările inițiate de orice aplicație din sistem."</string>
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;Fără titlu&gt;"</string>
<string name="notification_download_complete" msgid="5443563299253103667">"Descărcare finalizată."</string>
- <string name="notification_download_failed" msgid="8612136111952014978">"Descărcare nereuşită."</string>
+ <string name="notification_download_failed" msgid="8612136111952014978">"Descărcare nereușită."</string>
<string name="notification_need_wifi_for_size" msgid="2556172885154833575">"Dimen. desc. nec. Wi-Fi"</string>
<string name="notification_paused_in_background" msgid="4328508073283591772">"Întreruptă în fundal."</string>
- <string name="wifi_required_title" msgid="1995971416871498179">"Descărcare prea mare pentru reţeaua operatorului"</string>
- <string name="wifi_required_body" msgid="3067694630143784449">"Trebuie să utilizaţi Wi-Fi pentru a finaliza această descărcare de <xliff:g id="SIZE">%s </xliff:g>. \n\nAtingeţi <xliff:g id="QUEUE_TEXT">%s </xliff:g> pentru a începe această descărcare data viitoare când vă conectaţi la o rețea Wi-Fi."</string>
+ <string name="wifi_required_title" msgid="1995971416871498179">"Descărcare prea mare pentru rețeaua operatorului"</string>
+ <string name="wifi_required_body" msgid="3067694630143784449">"Trebuie să utilizați Wi-Fi pentru a finaliza această descărcare de <xliff:g id="SIZE">%s </xliff:g>. \n\nAtingeți <xliff:g id="QUEUE_TEXT">%s </xliff:g> pentru a începe această descărcare data viitoare când vă conectați la o rețea Wi-Fi."</string>
<string name="wifi_recommended_title" msgid="7441589306734687400">"Puneți în așteptare pentru descărcare ulterioară?"</string>
- <string name="wifi_recommended_body" msgid="1314735166699936073">"Pornirea ac.desc.de <xliff:g id="SIZE">%s </xliff:g> acum impl.scurt.dur.de viaţă a bater.și/sau utiliz.exces.a conex.de date mob.și tax.suplim.din partea oper.de telef.mob.,cf.plan.de date.\n\n Ating. <xliff:g id="QUEUE_TEXT">%s</xliff:g> pt.a înc.desc.la conect.la Wi-Fi."</string>
+ <string name="wifi_recommended_body" msgid="1314735166699936073">"Pornirea ac.desc.de <xliff:g id="SIZE">%s </xliff:g> acum impl.scurt.dur.de viață a bater.și/sau utiliz.exces.a conex.de date mob.și tax.suplim.din partea oper.de telef.mob.,cf.plan.de date.\n\n Ating. <xliff:g id="QUEUE_TEXT">%s</xliff:g> pt.a înc.desc.la conect.la Wi-Fi."</string>
<string name="button_queue_for_wifi" msgid="422576726189179221">"Listă de așteptare"</string>
<string name="button_cancel_download" msgid="2430166148737975604">"Anulați"</string>
- <string name="button_start_now" msgid="792123674007840864">"Începeţi acum"</string>
+ <string name="button_start_now" msgid="792123674007840864">"Începeți acum"</string>
<plurals name="notif_summary_active" formatted="false" msgid="7290448463204837173">
<item quantity="few">Se descarcă <xliff:g id="NUMBER">%d</xliff:g> fișiere</item>
<item quantity="other">Se descarcă <xliff:g id="NUMBER">%d</xliff:g> de fișiere</item>
@@ -55,7 +55,7 @@
<item quantity="one">Un fișier în așteptare</item>
</plurals>
<string name="download_remaining" msgid="3139295890887972718">"Timp rămas: <xliff:g id="DURATION">%s</xliff:g>"</string>
- <string name="download_no_application_title" msgid="7935659741162801699">"Fişierul nu poate fi deschis"</string>
+ <string name="download_no_application_title" msgid="7935659741162801699">"Fișierul nu poate fi deschis"</string>
<string name="root_downloads" msgid="4098414876292351487">"Descărcări"</string>
<string name="download_queued" msgid="3302638231377947451">"În coadă"</string>
<string name="download_running" msgid="3925050393361158266">"În desfășurare"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 0f15f839..c9c7df34 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-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 6c0150cc..d29ec693 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -16,21 +16,21 @@
<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">"Yuklanishlar menejeri"</string>
+ <string name="app_label" msgid="3658948994665187911">"Yuklanmalar boshqaruvi"</string>
<string name="permlab_downloadManager" msgid="7779544811202855500">"Yuklab olish menejeriga kirishga ruxsat"</string>
<string name="permdesc_downloadManager" msgid="4237406545998908947">"Ilovaga yuklab olish menejeriga kirish va yuklab olish fayllaridan foydalanish uchun ruxsat beradi. Zararli dasturlar bundan yuklab olishlarga to‘sqinlik qilish va maxfiy ma’lumotlarga kirish uchun foydalanishi mumkin."</string>
<string name="permlab_downloadManagerAdvanced" msgid="7103642833308809655">"Qo‘shimcha yuklab olish menejeri funksiyalari."</string>
<string name="permdesc_downloadManagerAdvanced" msgid="2659546004160962761">"Ilovaga yuklab olish menejerining qo‘shimcha funksiyalariga kirish uchun ruxsat beradi. Zararli dasturlar bundan yuklab olishlarga to‘sqinlik qilish va maxfiy ma’lumotlarga kirish uchun foydalanishi mumkin."</string>
<string name="permlab_downloadCompletedIntent" msgid="945913803765675685">"Yuklab olish xabarnomalarini jo‘natadi."</string>
<string name="permdesc_downloadCompletedIntent" msgid="2094706189855699533">"Ilovaga tugagan yuklab olishlar haqidagi xabarnomalarini jo‘natishga ruxsat beradi. Zararli dasturlar bundan yuklab olinayotgan fayllarni boshqalari bilan chalkashtirish uchun foydalanishi mumkin."</string>
- <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"USB’dagi yuk. olish ko‘r."</string>
- <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"SD xotira kartasidagi barcha yuklab olishlarni ko‘rish"</string>
+ <string name="permlab_seeAllExternal" product="nosdcard" msgid="4084575448409212628">"USB qurilmadagi barcha yuklanmalarni ko‘rish"</string>
+ <string name="permlab_seeAllExternal" product="default" msgid="140058400609165726">"SD kartadagi barcha yuklanmalarni ko‘rish"</string>
<string name="permdesc_seeAllExternal" msgid="1672759909065511233">"Ilovaga qaysi ilovalar fayllarni SD xotira kartasiga yuklab olganidan qat’iy nazar, undagi barcha fayllarni ko‘rishga ruxsat beradi."</string>
<string name="permlab_downloadCacheNonPurgeable" msgid="3069534308882047412">"Yuklab olish keshida joy qoldirish"</string>
<string name="permdesc_downloadCacheNonPurgeable" msgid="2408760720334570420">"Yuklashlar menejeriga ko‘proq bo‘sh joy kerak bo‘lganda avtomatik o‘chirilmaydigan fayllarni yuklab olish keshiga yuklab olish uchun ilovaga ruxsat beradi."</string>
<string name="permlab_downloadWithoutNotification" msgid="8837971946078327262">"fayllarni ogohlantirishsiz yuklab olish"</string>
<string name="permdesc_downloadWithoutNotification" msgid="8483135034298639727">"Foydalanuvchini hech qanday ogohlantirmasdan Ilovaga yuklab olish menejeri orqali fayllar yuklab olish uchun ruxsat beradi."</string>
- <string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Tizimdagi barcha yuklab olishlariga ruxsat berish"</string>
+ <string name="permlab_accessAllDownloads" msgid="2436240495424393717">"Tizimdagi barcha yuklanmalarga ruxsat berish"</string>
<string name="permdesc_accessAllDownloads" msgid="1871832254578267128">"Ilovaga tizimda har qanday ilova tomonidan ishga tushirilgan barcha yuklanishlarni ko‘rish va o‘zgartirish uchun ruxsat beradi."</string>
<string name="download_unknown_title" msgid="7015124071247271585">"&lt;Sarlavhasiz&gt;"</string>
<string name="notification_download_complete" msgid="5443563299253103667">"Yuklab olish tugallandi."</string>
@@ -52,9 +52,9 @@
<item quantity="other"><xliff:g id="NUMBER">%d</xliff:g> ta fayl navbatda</item>
<item quantity="one">1 ta fayl navbatda</item>
</plurals>
- <string name="download_remaining" msgid="3139295890887972718">"<xliff:g id="DURATION">%s</xliff:g> o‘tib ketdi"</string>
+ <string name="download_remaining" msgid="3139295890887972718">"<xliff:g id="DURATION">%s</xliff:g> qoldi"</string>
<string name="download_no_application_title" msgid="7935659741162801699">"Fayl ochilmadi"</string>
- <string name="root_downloads" msgid="4098414876292351487">"Yuklanishlar"</string>
+ <string name="root_downloads" msgid="4098414876292351487">"Yuklanmalar"</string>
<string name="download_queued" msgid="3302638231377947451">"Navbatga qo‘shildi"</string>
<string name="download_running" msgid="3925050393361158266">"Yuklab olinmoqda"</string>
<string name="download_error" msgid="5144180777324573236">"Muvaffaqiyatsiz"</string>
diff --git a/src/com/android/providers/downloads/Constants.java b/src/com/android/providers/downloads/Constants.java
index 7b8fcd24..79daeaed 100644
--- a/src/com/android/providers/downloads/Constants.java
+++ b/src/com/android/providers/downloads/Constants.java
@@ -45,15 +45,15 @@ public class Constants {
/** The column that is used for the initiating app's UID */
public static final String UID = "uid";
- /** The intent that gets sent when the service must wake up for a retry */
- public static final String ACTION_RETRY = "android.intent.action.DOWNLOAD_WAKEUP";
-
/** the intent that gets sent when clicking a successful download */
public static final String ACTION_OPEN = "android.intent.action.DOWNLOAD_OPEN";
/** the intent that gets sent when clicking an incomplete/failed download */
public static final String ACTION_LIST = "android.intent.action.DOWNLOAD_LIST";
+ /** the intent that gets sent when canceling a download */
+ public static final String ACTION_CANCEL = "android.intent.action.DOWNLOAD_CANCEL";
+
/** the intent that gets sent when deleting the notification of a completed download */
public static final String ACTION_HIDE = "android.intent.action.DOWNLOAD_HIDE";
diff --git a/src/com/android/providers/downloads/DownloadIdleService.java b/src/com/android/providers/downloads/DownloadIdleService.java
index b5371552..ecebb0f8 100644
--- a/src/com/android/providers/downloads/DownloadIdleService.java
+++ b/src/com/android/providers/downloads/DownloadIdleService.java
@@ -20,10 +20,14 @@ import static com.android.providers.downloads.Constants.TAG;
import static com.android.providers.downloads.StorageUtils.listFilesRecursive;
import android.app.DownloadManager;
+import android.app.job.JobInfo;
import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
import android.app.job.JobService;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentUris;
+import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.provider.Downloads;
@@ -33,11 +37,12 @@ import android.text.format.DateUtils;
import android.util.Slog;
import com.android.providers.downloads.StorageUtils.ConcreteFile;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Sets;
import libcore.io.IoUtils;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
+
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
@@ -48,6 +53,7 @@ import java.util.HashSet;
* deleted directly on disk.
*/
public class DownloadIdleService extends JobService {
+ private static final int IDLE_JOB_ID = -100;
private class IdleRunnable implements Runnable {
private JobParameters mParams;
@@ -66,7 +72,7 @@ public class DownloadIdleService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
- new Thread(new IdleRunnable(params)).start();
+ Helpers.getAsyncHandler().post(new IdleRunnable(params));
return true;
}
@@ -77,6 +83,19 @@ public class DownloadIdleService extends JobService {
return false;
}
+ public static void scheduleIdlePass(Context context) {
+ final JobScheduler scheduler = context.getSystemService(JobScheduler.class);
+ if (scheduler.getPendingJob(IDLE_JOB_ID) == null) {
+ final JobInfo job = new JobInfo.Builder(IDLE_JOB_ID,
+ new ComponentName(context, DownloadIdleService.class))
+ .setPeriodic(12 * DateUtils.HOUR_IN_MILLIS)
+ .setRequiresCharging(true)
+ .setRequiresDeviceIdle(true)
+ .build();
+ scheduler.schedule(job);
+ }
+ }
+
private interface StaleQuery {
final String[] PROJECTION = new String[] {
Downloads.Impl._ID,
diff --git a/src/com/android/providers/downloads/DownloadInfo.java b/src/com/android/providers/downloads/DownloadInfo.java
index bee5c4a9..c571de4d 100644
--- a/src/com/android/providers/downloads/DownloadInfo.java
+++ b/src/com/android/providers/downloads/DownloadInfo.java
@@ -16,27 +16,26 @@
package com.android.providers.downloads;
+import static android.provider.Downloads.Impl.VISIBILITY_VISIBLE;
+import static android.provider.Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
+
import static com.android.providers.downloads.Constants.TAG;
import android.app.DownloadManager;
+import android.app.job.JobInfo;
import android.content.ContentResolver;
import android.content.ContentUris;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.Uri;
import android.os.Environment;
import android.provider.Downloads;
-import android.provider.Downloads.Impl;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import java.io.CharArrayWriter;
@@ -45,9 +44,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
/**
* Details about a specific download. Fields should only be mutated by updating
@@ -66,14 +62,6 @@ public class DownloadInfo {
mCursor = cursor;
}
- public DownloadInfo newDownloadInfo(
- Context context, SystemFacade systemFacade, DownloadNotifier notifier) {
- final DownloadInfo info = new DownloadInfo(context, systemFacade, notifier);
- updateFromDatabase(info);
- readRequestHeaders(info);
- return info;
- }
-
public void updateFromDatabase(DownloadInfo info) {
info.mId = getLong(Downloads.Impl._ID);
info.mUri = getString(Downloads.Impl.COLUMN_URI);
@@ -105,6 +93,7 @@ public class DownloadInfo {
info.mAllowedNetworkTypes = getInt(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES);
info.mAllowRoaming = getInt(Downloads.Impl.COLUMN_ALLOW_ROAMING) != 0;
info.mAllowMetered = getInt(Downloads.Impl.COLUMN_ALLOW_METERED) != 0;
+ info.mFlags = getInt(Downloads.Impl.COLUMN_FLAGS);
info.mTitle = getString(Downloads.Impl.COLUMN_TITLE);
info.mDescription = getString(Downloads.Impl.COLUMN_DESCRIPTION);
info.mBypassRecommendedSizeLimit =
@@ -115,7 +104,7 @@ public class DownloadInfo {
}
}
- private void readRequestHeaders(DownloadInfo info) {
+ public void readRequestHeaders(DownloadInfo info) {
info.mRequestHeaders.clear();
Uri headerUri = Uri.withAppendedPath(
info.getAllDownloadsUri(), Downloads.Impl.RequestHeaders.URI_SEGMENT);
@@ -159,56 +148,6 @@ public class DownloadInfo {
}
}
- /**
- * Constants used to indicate network state for a specific download, after
- * applying any requested constraints.
- */
- public enum NetworkState {
- /**
- * The network is usable for the given download.
- */
- OK,
-
- /**
- * There is no network connectivity.
- */
- NO_CONNECTION,
-
- /**
- * The download exceeds the maximum size for this network.
- */
- UNUSABLE_DUE_TO_SIZE,
-
- /**
- * The download exceeds the recommended maximum size for this network,
- * the user must confirm for this download to proceed without WiFi.
- */
- RECOMMENDED_UNUSABLE_DUE_TO_SIZE,
-
- /**
- * The current connection is roaming, and the download can't proceed
- * over a roaming connection.
- */
- CANNOT_USE_ROAMING,
-
- /**
- * The app requesting the download specific that it can't use the
- * current network connection.
- */
- TYPE_DISALLOWED_BY_REQUESTOR,
-
- /**
- * Current network is blocked for requesting application.
- */
- BLOCKED;
- }
-
- /**
- * For intents used to notify the user that a download exceeds a size threshold, if this extra
- * is true, WiFi is required for this download size; otherwise, it is only recommended.
- */
- public static final String EXTRA_IS_WIFI_REQUIRED = "isWifiRequired";
-
public long mId;
public String mUri;
@Deprecated
@@ -240,33 +179,35 @@ public class DownloadInfo {
public int mAllowedNetworkTypes;
public boolean mAllowRoaming;
public boolean mAllowMetered;
+ public int mFlags;
public String mTitle;
public String mDescription;
public int mBypassRecommendedSizeLimit;
- public int mFuzz;
-
private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>();
- /**
- * Result of last {@link DownloadThread} started by
- * {@link #startDownloadIfReady(ExecutorService)}.
- */
- @GuardedBy("this")
- private Future<?> mSubmittedTask;
-
- @GuardedBy("this")
- private DownloadThread mTask;
-
private final Context mContext;
private final SystemFacade mSystemFacade;
- private final DownloadNotifier mNotifier;
- private DownloadInfo(Context context, SystemFacade systemFacade, DownloadNotifier notifier) {
+ public DownloadInfo(Context context) {
mContext = context;
- mSystemFacade = systemFacade;
- mNotifier = notifier;
- mFuzz = Helpers.sRandom.nextInt(1001);
+ mSystemFacade = Helpers.getSystemFacade(context);
+ }
+
+ public static DownloadInfo queryDownloadInfo(Context context, long downloadId) {
+ final ContentResolver resolver = context.getContentResolver();
+ try (Cursor cursor = resolver.query(
+ ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, downloadId),
+ null, null, null, null)) {
+ final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver, cursor);
+ final DownloadInfo info = new DownloadInfo(context);
+ if (cursor.moveToFirst()) {
+ reader.updateFromDatabase(info);
+ reader.readRequestHeaders(info);
+ return info;
+ }
+ }
+ return null;
}
public Collection<Pair<String, String>> getHeaders() {
@@ -309,43 +250,93 @@ public class DownloadInfo {
}
/**
- * Returns the time when a download should be restarted.
+ * Return if this download is visible to the user while running.
+ */
+ public boolean isVisible() {
+ switch (mVisibility) {
+ case VISIBILITY_VISIBLE:
+ case VISIBILITY_VISIBLE_NOTIFY_COMPLETED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Add random fuzz to the given delay so it's anywhere between 1-1.5x the
+ * requested delay.
+ */
+ private long fuzzDelay(long delay) {
+ return delay + Helpers.sRandom.nextInt((int) (delay / 2));
+ }
+
+ /**
+ * Return minimum latency in milliseconds required before this download is
+ * allowed to start again.
+ *
+ * @see android.app.job.JobInfo.Builder#setMinimumLatency(long)
*/
- public long restartTime(long now) {
- if (mNumFailed == 0) {
- return now;
+ public long getMinimumLatency() {
+ if (mStatus == Downloads.Impl.STATUS_WAITING_TO_RETRY) {
+ final long now = mSystemFacade.currentTimeMillis();
+ final long startAfter;
+ if (mNumFailed == 0) {
+ startAfter = now;
+ } else if (mRetryAfter > 0) {
+ startAfter = mLastMod + fuzzDelay(mRetryAfter);
+ } else {
+ final long delay = (Constants.RETRY_FIRST_DELAY * DateUtils.SECOND_IN_MILLIS
+ * (1 << (mNumFailed - 1)));
+ startAfter = mLastMod + fuzzDelay(delay);
+ }
+ return Math.max(0, startAfter - now);
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Return the network type constraint required by this download.
+ *
+ * @see android.app.job.JobInfo.Builder#setRequiredNetworkType(int)
+ */
+ public int getRequiredNetworkType(long totalBytes) {
+ if (!mAllowMetered) {
+ return JobInfo.NETWORK_TYPE_UNMETERED;
+ }
+ if (mAllowedNetworkTypes == DownloadManager.Request.NETWORK_WIFI) {
+ return JobInfo.NETWORK_TYPE_UNMETERED;
+ }
+ if (totalBytes > mSystemFacade.getMaxBytesOverMobile()) {
+ return JobInfo.NETWORK_TYPE_UNMETERED;
}
- if (mRetryAfter > 0) {
- return mLastMod + mRetryAfter;
+ if (totalBytes > mSystemFacade.getRecommendedMaxBytesOverMobile()
+ && mBypassRecommendedSizeLimit == 0) {
+ return JobInfo.NETWORK_TYPE_UNMETERED;
}
- return mLastMod +
- Constants.RETRY_FIRST_DELAY *
- (1000 + mFuzz) * (1 << (mNumFailed - 1));
+ if (!mAllowRoaming) {
+ return JobInfo.NETWORK_TYPE_NOT_ROAMING;
+ }
+ return JobInfo.NETWORK_TYPE_ANY;
}
/**
- * Returns whether this download should be enqueued.
+ * Returns whether this download is ready to be scheduled.
*/
- private boolean isReadyToDownload() {
+ public boolean isReadyToSchedule() {
if (mControl == Downloads.Impl.CONTROL_PAUSED) {
// the download is paused, so it's not going to start
return false;
}
switch (mStatus) {
- case 0: // status hasn't been initialized yet, this is a new download
- case Downloads.Impl.STATUS_PENDING: // download is explicit marked as ready to start
- case Downloads.Impl.STATUS_RUNNING: // download interrupted (process killed etc) while
- // running, without a chance to update the database
- return true;
-
+ case 0:
+ case Downloads.Impl.STATUS_PENDING:
+ case Downloads.Impl.STATUS_RUNNING:
case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
+ case Downloads.Impl.STATUS_WAITING_TO_RETRY:
case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
- return checkCanUseNetwork(mTotalBytes) == NetworkState.OK;
+ return true;
- case Downloads.Impl.STATUS_WAITING_TO_RETRY:
- // download was waiting for a delayed restart
- final long now = mSystemFacade.currentTimeMillis();
- return restartTime(now) <= now;
case Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR:
// is the media mounted?
final Uri uri = Uri.parse(mUri);
@@ -357,11 +348,10 @@ public class DownloadInfo {
Log.w(TAG, "Expected file URI on external storage: " + mUri);
return false;
}
- case Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR:
- // avoids repetition of retrying download
+
+ default:
return false;
}
- return false;
}
/**
@@ -378,27 +368,11 @@ public class DownloadInfo {
return false;
}
- /**
- * Returns whether this download is allowed to use the network.
- */
- public NetworkState checkCanUseNetwork(long totalBytes) {
- final NetworkInfo info = mSystemFacade.getActiveNetworkInfo(mUid);
- if (info == null || !info.isConnected()) {
- return NetworkState.NO_CONNECTION;
- }
- if (DetailedState.BLOCKED.equals(info.getDetailedState())) {
- return NetworkState.BLOCKED;
- }
- if (mSystemFacade.isNetworkRoaming() && !isRoamingAllowed()) {
- return NetworkState.CANNOT_USE_ROAMING;
- }
- if (mSystemFacade.isActiveNetworkMetered() && !mAllowMetered) {
- return NetworkState.TYPE_DISALLOWED_BY_REQUESTOR;
- }
- return checkIsNetworkTypeAllowed(info.getType(), totalBytes);
+ public boolean isMeteredAllowed(long totalBytes) {
+ return getRequiredNetworkType(totalBytes) != JobInfo.NETWORK_TYPE_UNMETERED;
}
- private boolean isRoamingAllowed() {
+ public boolean isRoamingAllowed() {
if (mIsPublicApi) {
return mAllowRoaming;
} else { // legacy behavior
@@ -406,112 +380,6 @@ public class DownloadInfo {
}
}
- /**
- * Check if this download can proceed over the given network type.
- * @param networkType a constant from ConnectivityManager.TYPE_*.
- * @return one of the NETWORK_* constants
- */
- private NetworkState checkIsNetworkTypeAllowed(int networkType, long totalBytes) {
- if (mIsPublicApi) {
- final int flag = translateNetworkTypeToApiFlag(networkType);
- final boolean allowAllNetworkTypes = mAllowedNetworkTypes == ~0;
- if (!allowAllNetworkTypes && (flag & mAllowedNetworkTypes) == 0) {
- return NetworkState.TYPE_DISALLOWED_BY_REQUESTOR;
- }
- }
- return checkSizeAllowedForNetwork(networkType, totalBytes);
- }
-
- /**
- * Translate a ConnectivityManager.TYPE_* constant to the corresponding
- * DownloadManager.Request.NETWORK_* bit flag.
- */
- private int translateNetworkTypeToApiFlag(int networkType) {
- switch (networkType) {
- case ConnectivityManager.TYPE_MOBILE:
- return DownloadManager.Request.NETWORK_MOBILE;
-
- case ConnectivityManager.TYPE_WIFI:
- return DownloadManager.Request.NETWORK_WIFI;
-
- case ConnectivityManager.TYPE_BLUETOOTH:
- return DownloadManager.Request.NETWORK_BLUETOOTH;
-
- default:
- return 0;
- }
- }
-
- /**
- * Check if the download's size prohibits it from running over the current network.
- * @return one of the NETWORK_* constants
- */
- private NetworkState checkSizeAllowedForNetwork(int networkType, long totalBytes) {
- if (totalBytes <= 0) {
- // we don't know the size yet
- return NetworkState.OK;
- }
-
- if (ConnectivityManager.isNetworkTypeMobile(networkType)) {
- Long maxBytesOverMobile = mSystemFacade.getMaxBytesOverMobile();
- if (maxBytesOverMobile != null && totalBytes > maxBytesOverMobile) {
- return NetworkState.UNUSABLE_DUE_TO_SIZE;
- }
- if (mBypassRecommendedSizeLimit == 0) {
- Long recommendedMaxBytesOverMobile = mSystemFacade
- .getRecommendedMaxBytesOverMobile();
- if (recommendedMaxBytesOverMobile != null
- && totalBytes > recommendedMaxBytesOverMobile) {
- return NetworkState.RECOMMENDED_UNUSABLE_DUE_TO_SIZE;
- }
- }
- }
-
- return NetworkState.OK;
- }
-
- /**
- * If download is ready to start, and isn't already pending or executing,
- * create a {@link DownloadThread} and enqueue it into given
- * {@link Executor}.
- *
- * @return If actively downloading.
- */
- public boolean startDownloadIfReady(ExecutorService executor) {
- synchronized (this) {
- final boolean isReady = isReadyToDownload();
- final boolean isActive = mSubmittedTask != null && !mSubmittedTask.isDone();
- if (isReady && !isActive) {
- if (mStatus != Impl.STATUS_RUNNING) {
- mStatus = Impl.STATUS_RUNNING;
- ContentValues values = new ContentValues();
- values.put(Impl.COLUMN_STATUS, mStatus);
- mContext.getContentResolver().update(getAllDownloadsUri(), values, null, null);
- }
-
- mTask = new DownloadThread(mContext, mSystemFacade, mNotifier, this);
- mSubmittedTask = executor.submit(mTask);
- }
- return isReady;
- }
- }
-
- /**
- * If download is ready to be scanned, enqueue it into the given
- * {@link DownloadScanner}.
- *
- * @return If actively scanning.
- */
- public boolean startScanIfReady(DownloadScanner scanner) {
- synchronized (this) {
- final boolean isReady = shouldScanFile();
- if (isReady) {
- scanner.requestScan(this);
- }
- return isReady;
- }
- }
-
public boolean isOnCache() {
return (mDestination == Downloads.Impl.DESTINATION_CACHE_PARTITION
|| mDestination == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION
@@ -571,70 +439,42 @@ public class DownloadInfo {
pw.printPair("mAllowedNetworkTypes", mAllowedNetworkTypes);
pw.printPair("mAllowRoaming", mAllowRoaming);
pw.printPair("mAllowMetered", mAllowMetered);
+ pw.printPair("mFlags", mFlags);
pw.println();
pw.decreaseIndent();
}
/**
- * Return time when this download will be ready for its next action, in
- * milliseconds after given time.
- *
- * @return If {@code 0}, download is ready to proceed immediately. If
- * {@link Long#MAX_VALUE}, then download has no future actions.
- */
- public long nextActionMillis(long now) {
- if (Downloads.Impl.isStatusCompleted(mStatus)) {
- return Long.MAX_VALUE;
- }
- if (mStatus != Downloads.Impl.STATUS_WAITING_TO_RETRY) {
- return 0;
- }
- long when = restartTime(now);
- if (when <= now) {
- return 0;
- }
- return when - now;
- }
-
- /**
* Returns whether a file should be scanned
*/
- public boolean shouldScanFile() {
+ public boolean shouldScanFile(int status) {
return (mMediaScanned == 0)
&& (mDestination == Downloads.Impl.DESTINATION_EXTERNAL ||
mDestination == Downloads.Impl.DESTINATION_FILE_URI ||
mDestination == Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD)
- && Downloads.Impl.isStatusSuccess(mStatus);
- }
-
- void notifyPauseDueToSize(boolean isWifiRequired) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(getAllDownloadsUri());
- intent.setClassName(SizeLimitActivity.class.getPackage().getName(),
- SizeLimitActivity.class.getName());
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(EXTRA_IS_WIFI_REQUIRED, isWifiRequired);
- mContext.startActivity(intent);
+ && Downloads.Impl.isStatusSuccess(status);
}
/**
* 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 {
+ public int queryDownloadStatus() {
+ return queryDownloadInt(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
+ }
+
+ public int queryDownloadControl() {
+ return queryDownloadInt(Downloads.Impl.COLUMN_CONTROL, Downloads.Impl.CONTROL_RUN);
+ }
+
+ public int queryDownloadInt(String columnName, int defaultValue) {
+ try (Cursor cursor = mContext.getContentResolver().query(getAllDownloadsUri(),
+ new String[] { columnName }, null, null, null)) {
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;
+ return defaultValue;
}
- } finally {
- cursor.close();
}
}
}
diff --git a/src/com/android/providers/downloads/DownloadJobService.java b/src/com/android/providers/downloads/DownloadJobService.java
new file mode 100644
index 00000000..0ce4266a
--- /dev/null
+++ b/src/com/android/providers/downloads/DownloadJobService.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 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.provider.Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI;
+
+import static com.android.providers.downloads.Constants.TAG;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.database.ContentObserver;
+import android.util.Log;
+import android.util.SparseArray;
+
+/**
+ * Service that hosts download jobs. Each active download job is handled as a
+ * unique {@link DownloadThread} instance.
+ * <p>
+ * The majority of downloads should have ETag values to enable resuming, so if a
+ * given download isn't able to finish in the normal job timeout (10 minutes),
+ * we just reschedule the job and resume again in the future.
+ */
+public class DownloadJobService extends JobService {
+ // @GuardedBy("mActiveThreads")
+ private SparseArray<DownloadThread> mActiveThreads = new SparseArray<>();
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ // While someone is bound to us, watch for database changes that should
+ // trigger notification updates.
+ getContentResolver().registerContentObserver(ALL_DOWNLOADS_CONTENT_URI, true, mObserver);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ getContentResolver().unregisterContentObserver(mObserver);
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ final int id = params.getJobId();
+
+ // Spin up thread to handle this download
+ final DownloadInfo info = DownloadInfo.queryDownloadInfo(this, id);
+ if (info == null) {
+ Log.w(TAG, "Odd, no details found for download " + id);
+ return false;
+ }
+
+ final DownloadThread thread;
+ synchronized (mActiveThreads) {
+ thread = new DownloadThread(this, params, info);
+ mActiveThreads.put(id, thread);
+ }
+ thread.start();
+
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ final int id = params.getJobId();
+
+ final DownloadThread thread;
+ synchronized (mActiveThreads) {
+ thread = mActiveThreads.removeReturnOld(id);
+ }
+ if (thread != null) {
+ // If the thread is still running, ask it to gracefully shutdown,
+ // and reschedule ourselves to resume in the future.
+ thread.requestShutdown();
+
+ Helpers.scheduleJob(this, DownloadInfo.queryDownloadInfo(this, id));
+ }
+ return false;
+ }
+
+ public void jobFinishedInternal(JobParameters params, boolean needsReschedule) {
+ synchronized (mActiveThreads) {
+ mActiveThreads.remove(params.getJobId());
+ }
+
+ // Update notifications one last time while job is protecting us
+ mObserver.onChange(false);
+
+ jobFinished(params, needsReschedule);
+ }
+
+ private ContentObserver mObserver = new ContentObserver(Helpers.getAsyncHandler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ Helpers.getDownloadNotifier(DownloadJobService.this).update();
+ }
+ };
+}
diff --git a/src/com/android/providers/downloads/DownloadNotifier.java b/src/com/android/providers/downloads/DownloadNotifier.java
index 60c249f9..d5808690 100644
--- a/src/com/android/providers/downloads/DownloadNotifier.java
+++ b/src/com/android/providers/downloads/DownloadNotifier.java
@@ -19,7 +19,9 @@ package com.android.providers.downloads;
import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE;
import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION;
+import static android.provider.Downloads.Impl.STATUS_QUEUED_FOR_WIFI;
import static android.provider.Downloads.Impl.STATUS_RUNNING;
+
import static com.android.providers.downloads.Constants.TAG;
import android.app.DownloadManager;
@@ -30,28 +32,27 @@ import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.database.Cursor;
import android.net.Uri;
import android.os.SystemClock;
import android.provider.Downloads;
+import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.text.format.DateUtils;
+import android.util.ArrayMap;
+import android.util.IntArray;
import android.util.Log;
import android.util.LongSparseLongArray;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
+import com.android.internal.util.ArrayUtils;
import java.text.NumberFormat;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
import javax.annotation.concurrent.GuardedBy;
/**
- * Update {@link NotificationManager} to reflect current {@link DownloadInfo}
- * states. Collapses similar downloads into a single notification, and builds
+ * Update {@link NotificationManager} to reflect current download states.
+ * Collapses similar downloads into a single notification, and builds
* {@link PendingIntent} that launch towards {@link DownloadReceiver}.
*/
public class DownloadNotifier {
@@ -67,20 +68,20 @@ public class DownloadNotifier {
* Currently active notifications, mapped from clustering tag to timestamp
* when first shown.
*
- * @see #buildNotificationTag(DownloadInfo)
+ * @see #buildNotificationTag(Cursor)
*/
@GuardedBy("mActiveNotifs")
- private final HashMap<String, Long> mActiveNotifs = Maps.newHashMap();
+ private final ArrayMap<String, Long> mActiveNotifs = new ArrayMap<>();
/**
- * Current speed of active downloads, mapped from {@link DownloadInfo#mId}
- * to speed in bytes per second.
+ * Current speed of active downloads, mapped from download ID to speed in
+ * bytes per second.
*/
@GuardedBy("mDownloadSpeed")
private final LongSparseLongArray mDownloadSpeed = new LongSparseLongArray();
/**
- * Last time speed was reproted, mapped from {@link DownloadInfo#mId} to
+ * Last time speed was reproted, mapped from download ID to
* {@link SystemClock#elapsedRealtime()}.
*/
@GuardedBy("mDownloadSpeed")
@@ -92,8 +93,16 @@ public class DownloadNotifier {
Context.NOTIFICATION_SERVICE);
}
- public void cancelAll() {
- mNotifManager.cancelAll();
+ public void init() {
+ synchronized (mActiveNotifs) {
+ mActiveNotifs.clear();
+ final StatusBarNotification[] notifs = mNotifManager.getActiveNotifications();
+ if (!ArrayUtils.isEmpty(notifs)) {
+ for (StatusBarNotification notif : notifs) {
+ mActiveNotifs.put(notif.getTag(), notif.getPostTime());
+ }
+ }
+ }
}
/**
@@ -112,32 +121,62 @@ public class DownloadNotifier {
}
}
- /**
- * Update {@link NotificationManager} to reflect the given set of
- * {@link DownloadInfo}, adding, collapsing, and removing as needed.
- */
- public void updateWith(Collection<DownloadInfo> downloads) {
- synchronized (mActiveNotifs) {
- updateWithLocked(downloads);
+ private interface UpdateQuery {
+ final String[] PROJECTION = new String[] {
+ Downloads.Impl._ID,
+ Downloads.Impl.COLUMN_STATUS,
+ Downloads.Impl.COLUMN_VISIBILITY,
+ Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
+ Downloads.Impl.COLUMN_CURRENT_BYTES,
+ Downloads.Impl.COLUMN_TOTAL_BYTES,
+ Downloads.Impl.COLUMN_DESTINATION,
+ Downloads.Impl.COLUMN_TITLE,
+ Downloads.Impl.COLUMN_DESCRIPTION,
+ };
+
+ final int _ID = 0;
+ final int STATUS = 1;
+ final int VISIBILITY = 2;
+ final int NOTIFICATION_PACKAGE = 3;
+ final int CURRENT_BYTES = 4;
+ final int TOTAL_BYTES = 5;
+ final int DESTINATION = 6;
+ final int TITLE = 7;
+ final int DESCRIPTION = 8;
+ }
+
+ public void update() {
+ try (Cursor cursor = mContext.getContentResolver().query(
+ Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, UpdateQuery.PROJECTION,
+ Downloads.Impl.COLUMN_DELETED + " == '0'", null, null)) {
+ synchronized (mActiveNotifs) {
+ updateWithLocked(cursor);
+ }
}
}
- private void updateWithLocked(Collection<DownloadInfo> downloads) {
+ private void updateWithLocked(Cursor cursor) {
final Resources res = mContext.getResources();
// Cluster downloads together
- final Multimap<String, DownloadInfo> clustered = ArrayListMultimap.create();
- for (DownloadInfo info : downloads) {
- final String tag = buildNotificationTag(info);
+ final ArrayMap<String, IntArray> clustered = new ArrayMap<>();
+ while (cursor.moveToNext()) {
+ final String tag = buildNotificationTag(cursor);
if (tag != null) {
- clustered.put(tag, info);
+ IntArray cluster = clustered.get(tag);
+ if (cluster == null) {
+ cluster = new IntArray();
+ clustered.put(tag, cluster);
+ }
+ cluster.add(cursor.getPosition());
}
}
// Build notification for each cluster
- for (String tag : clustered.keySet()) {
+ for (int i = 0; i < clustered.size(); i++) {
+ final String tag = clustered.keyAt(i);
+ final IntArray cluster = clustered.valueAt(i);
final int type = getNotificationTagType(tag);
- final Collection<DownloadInfo> cluster = clustered.get(tag);
final Notification.Builder builder = new Notification.Builder(mContext);
builder.setColor(res.getColor(
@@ -164,27 +203,46 @@ public class DownloadNotifier {
// Build action intents
if (type == TYPE_ACTIVE || type == TYPE_WAITING) {
+ final long[] downloadIds = getDownloadIds(cursor, cluster);
+
// build a synthetic uri for intent identification purposes
final Uri uri = new Uri.Builder().scheme("active-dl").appendPath(tag).build();
final Intent intent = new Intent(Constants.ACTION_LIST,
uri, mContext, DownloadReceiver.class);
intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS,
- getDownloadIds(cluster));
+ downloadIds);
builder.setContentIntent(PendingIntent.getBroadcast(mContext,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
builder.setOngoing(true);
+ // Add a Cancel action
+ final Uri cancelUri = new Uri.Builder().scheme("cancel-dl").appendPath(tag).build();
+ final Intent cancelIntent = new Intent(Constants.ACTION_CANCEL,
+ cancelUri, mContext, DownloadReceiver.class);
+ cancelIntent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_IDS, downloadIds);
+ cancelIntent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_NOTIFICATION_TAG, tag);
+
+ builder.addAction(
+ android.R.drawable.ic_menu_close_clear_cancel,
+ res.getString(R.string.button_cancel_download),
+ PendingIntent.getBroadcast(mContext,
+ 0, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+
} else if (type == TYPE_COMPLETE) {
- final DownloadInfo info = cluster.iterator().next();
+ cursor.moveToPosition(cluster.get(0));
+ final long id = cursor.getLong(UpdateQuery._ID);
+ final int status = cursor.getInt(UpdateQuery.STATUS);
+ final int destination = cursor.getInt(UpdateQuery.DESTINATION);
+
final Uri uri = ContentUris.withAppendedId(
- Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, info.mId);
+ Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
builder.setAutoCancel(true);
final String action;
- if (Downloads.Impl.isStatusError(info.mStatus)) {
+ if (Downloads.Impl.isStatusError(status)) {
action = Constants.ACTION_LIST;
} else {
- if (info.mDestination != Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION) {
+ if (destination != Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION) {
action = Constants.ACTION_OPEN;
} else {
action = Constants.ACTION_LIST;
@@ -193,7 +251,7 @@ public class DownloadNotifier {
final Intent intent = new Intent(action, uri, mContext, DownloadReceiver.class);
intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS,
- getDownloadIds(cluster));
+ getDownloadIds(cursor, cluster));
builder.setContentIntent(PendingIntent.getBroadcast(mContext,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
@@ -210,11 +268,17 @@ public class DownloadNotifier {
long total = 0;
long speed = 0;
synchronized (mDownloadSpeed) {
- for (DownloadInfo info : cluster) {
- if (info.mTotalBytes != -1) {
- current += info.mCurrentBytes;
- total += info.mTotalBytes;
- speed += mDownloadSpeed.get(info.mId);
+ for (int j = 0; j < cluster.size(); j++) {
+ cursor.moveToPosition(cluster.get(j));
+
+ final long id = cursor.getLong(UpdateQuery._ID);
+ final long currentBytes = cursor.getLong(UpdateQuery.CURRENT_BYTES);
+ final long totalBytes = cursor.getLong(UpdateQuery.TOTAL_BYTES);
+
+ if (totalBytes != -1) {
+ current += currentBytes;
+ total += totalBytes;
+ speed += mDownloadSpeed.get(id);
}
}
}
@@ -239,13 +303,13 @@ public class DownloadNotifier {
// Build titles and description
final Notification notif;
if (cluster.size() == 1) {
- final DownloadInfo info = cluster.iterator().next();
-
- builder.setContentTitle(getDownloadTitle(res, info));
+ cursor.moveToPosition(cluster.get(0));
+ builder.setContentTitle(getDownloadTitle(res, cursor));
if (type == TYPE_ACTIVE) {
- if (!TextUtils.isEmpty(info.mDescription)) {
- builder.setContentText(info.mDescription);
+ final String description = cursor.getString(UpdateQuery.DESCRIPTION);
+ if (!TextUtils.isEmpty(description)) {
+ builder.setContentText(description);
} else {
builder.setContentText(remainingText);
}
@@ -256,9 +320,10 @@ public class DownloadNotifier {
res.getString(R.string.notification_need_wifi_for_size));
} else if (type == TYPE_COMPLETE) {
- if (Downloads.Impl.isStatusError(info.mStatus)) {
+ final int status = cursor.getInt(UpdateQuery.STATUS);
+ if (Downloads.Impl.isStatusError(status)) {
builder.setContentText(res.getText(R.string.notification_download_failed));
- } else if (Downloads.Impl.isStatusSuccess(info.mStatus)) {
+ } else if (Downloads.Impl.isStatusSuccess(status)) {
builder.setContentText(
res.getText(R.string.notification_download_complete));
}
@@ -269,8 +334,9 @@ public class DownloadNotifier {
} else {
final Notification.InboxStyle inboxStyle = new Notification.InboxStyle(builder);
- for (DownloadInfo info : cluster) {
- inboxStyle.addLine(getDownloadTitle(res, info));
+ for (int j = 0; j < cluster.size(); j++) {
+ cursor.moveToPosition(cluster.get(j));
+ inboxStyle.addLine(getDownloadTitle(res, cursor));
}
if (type == TYPE_ACTIVE) {
@@ -296,29 +362,31 @@ public class DownloadNotifier {
}
// Remove stale tags that weren't renewed
- final Iterator<String> it = mActiveNotifs.keySet().iterator();
- while (it.hasNext()) {
- final String tag = it.next();
- if (!clustered.containsKey(tag)) {
+ for (int i = 0; i < mActiveNotifs.size();) {
+ final String tag = mActiveNotifs.keyAt(i);
+ if (clustered.containsKey(tag)) {
+ i++;
+ } else {
mNotifManager.cancel(tag, 0);
- it.remove();
+ mActiveNotifs.removeAt(i);
}
}
}
- private static CharSequence getDownloadTitle(Resources res, DownloadInfo info) {
- if (!TextUtils.isEmpty(info.mTitle)) {
- return info.mTitle;
+ private static CharSequence getDownloadTitle(Resources res, Cursor cursor) {
+ final String title = cursor.getString(UpdateQuery.TITLE);
+ if (!TextUtils.isEmpty(title)) {
+ return title;
} else {
return res.getString(R.string.download_unknown_title);
}
}
- private long[] getDownloadIds(Collection<DownloadInfo> infos) {
- final long[] ids = new long[infos.size()];
- int i = 0;
- for (DownloadInfo info : infos) {
- ids[i++] = info.mId;
+ private long[] getDownloadIds(Cursor cursor, IntArray cluster) {
+ final long[] ids = new long[cluster.size()];
+ for (int i = 0; i < cluster.size(); i++) {
+ cursor.moveToPosition(cluster.get(i));
+ ids[i] = cursor.getLong(UpdateQuery._ID);
}
return ids;
}
@@ -335,17 +403,22 @@ public class DownloadNotifier {
}
/**
- * Build tag used for collapsing several {@link DownloadInfo} into a single
+ * Build tag used for collapsing several downloads into a single
* {@link Notification}.
*/
- private static String buildNotificationTag(DownloadInfo info) {
- if (info.mStatus == Downloads.Impl.STATUS_QUEUED_FOR_WIFI) {
- return TYPE_WAITING + ":" + info.mPackage;
- } else if (isActiveAndVisible(info)) {
- return TYPE_ACTIVE + ":" + info.mPackage;
- } else if (isCompleteAndVisible(info)) {
+ private static String buildNotificationTag(Cursor cursor) {
+ final long id = cursor.getLong(UpdateQuery._ID);
+ final int status = cursor.getInt(UpdateQuery.STATUS);
+ final int visibility = cursor.getInt(UpdateQuery.VISIBILITY);
+ final String notifPackage = cursor.getString(UpdateQuery.NOTIFICATION_PACKAGE);
+
+ if (isQueuedAndVisible(status, visibility)) {
+ return TYPE_WAITING + ":" + notifPackage;
+ } else if (isActiveAndVisible(status, visibility)) {
+ return TYPE_ACTIVE + ":" + notifPackage;
+ } else if (isCompleteAndVisible(status, visibility)) {
// Complete downloads always have unique notifs
- return TYPE_COMPLETE + ":" + info.mId;
+ return TYPE_COMPLETE + ":" + id;
} else {
return null;
}
@@ -353,21 +426,27 @@ public class DownloadNotifier {
/**
* Return the cluster type of the given tag, as created by
- * {@link #buildNotificationTag(DownloadInfo)}.
+ * {@link #buildNotificationTag(Cursor)}.
*/
private static int getNotificationTagType(String tag) {
return Integer.parseInt(tag.substring(0, tag.indexOf(':')));
}
- private static boolean isActiveAndVisible(DownloadInfo download) {
- return download.mStatus == STATUS_RUNNING &&
- (download.mVisibility == VISIBILITY_VISIBLE
- || download.mVisibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+ private static boolean isQueuedAndVisible(int status, int visibility) {
+ return status == STATUS_QUEUED_FOR_WIFI &&
+ (visibility == VISIBILITY_VISIBLE
+ || visibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+ }
+
+ private static boolean isActiveAndVisible(int status, int visibility) {
+ return status == STATUS_RUNNING &&
+ (visibility == VISIBILITY_VISIBLE
+ || visibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
- private static boolean isCompleteAndVisible(DownloadInfo download) {
- return Downloads.Impl.isStatusCompleted(download.mStatus) &&
- (download.mVisibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED
- || download.mVisibility == VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION);
+ private static boolean isCompleteAndVisible(int status, int visibility) {
+ return Downloads.Impl.isStatusCompleted(status) &&
+ (visibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED
+ || visibility == VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION);
}
}
diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java
index e79aff5f..d30018f7 100644
--- a/src/com/android/providers/downloads/DownloadProvider.java
+++ b/src/com/android/providers/downloads/DownloadProvider.java
@@ -16,9 +16,18 @@
package com.android.providers.downloads;
+import static android.provider.BaseColumns._ID;
+import static android.provider.Downloads.Impl.COLUMN_DESTINATION;
+import static android.provider.Downloads.Impl.COLUMN_MEDIAPROVIDER_URI;
+import static android.provider.Downloads.Impl.COLUMN_MEDIA_SCANNED;
+import static android.provider.Downloads.Impl.COLUMN_MIME_TYPE;
+import static android.provider.Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD;
+import static android.provider.Downloads.Impl._DATA;
+
import android.app.AppOpsManager;
import android.app.DownloadManager;
import android.app.DownloadManager.Request;
+import android.app.job.JobScheduler;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -35,8 +44,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
import android.os.Process;
@@ -47,9 +54,10 @@ import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
+import com.android.internal.util.IndentingPrintWriter;
+
import libcore.io.IoUtils;
-import com.android.internal.util.IndentingPrintWriter;
import com.google.android.collect.Maps;
import com.google.common.annotations.VisibleForTesting;
@@ -73,7 +81,7 @@ public final class DownloadProvider extends ContentProvider {
/** Database filename */
private static final String DB_NAME = "downloads.db";
/** Current database version */
- private static final int DB_VERSION = 109;
+ private static final int DB_VERSION = 110;
/** Name of table in the database */
private static final String DB_TABLE = "downloads";
@@ -170,7 +178,8 @@ public final class DownloadProvider extends ContentProvider {
private static final List<String> downloadManagerColumnsList =
Arrays.asList(DownloadManager.UNDERLYING_COLUMNS);
- private Handler mHandler;
+ @VisibleForTesting
+ SystemFacade mSystemFacade;
/** The database that lies underneath this content provider */
private SQLiteOpenHelper mOpenHelper = null;
@@ -179,9 +188,6 @@ public final class DownloadProvider extends ContentProvider {
private int mSystemUid = -1;
private int mDefContainerUid = -1;
- @VisibleForTesting
- SystemFacade mSystemFacade;
-
/**
* This class encapsulates a SQL where clause and its parameters. It makes it possible for
* shared methods (like {@link DownloadProvider#getWhereClause(Uri, String, String[], int)})
@@ -329,6 +335,11 @@ public final class DownloadProvider extends ContentProvider {
"BOOLEAN NOT NULL DEFAULT 0");
break;
+ case 110:
+ addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_FLAGS,
+ "INTEGER NOT NULL DEFAULT 0");
+ break;
+
default:
throw new IllegalStateException("Don't know how to upgrade to " + version);
}
@@ -442,11 +453,6 @@ public final class DownloadProvider extends ContentProvider {
mSystemFacade = new RealSystemFacade(getContext());
}
- HandlerThread handlerThread =
- new HandlerThread("DownloadProvider handler", Process.THREAD_PRIORITY_BACKGROUND);
- handlerThread.start();
- mHandler = new Handler(handlerThread.getLooper());
-
mOpenHelper = new DatabaseHelper(getContext());
// Initialize the system uid
mSystemUid = Process.SYSTEM_UID;
@@ -462,10 +468,6 @@ public final class DownloadProvider extends ContentProvider {
if (appInfo != null) {
mDefContainerUid = appInfo.uid;
}
- // start the DownloadService class. don't wait for the 1st download to be issued.
- // saves us by getting some initialization code in DownloadService out of the way.
- Context context = getContext();
- context.startService(new Intent(context, DownloadService.class));
return true;
}
@@ -669,6 +671,7 @@ public final class DownloadProvider extends ContentProvider {
copyInteger(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, values, filteredValues);
copyBoolean(Downloads.Impl.COLUMN_ALLOW_ROAMING, values, filteredValues);
copyBoolean(Downloads.Impl.COLUMN_ALLOW_METERED, values, filteredValues);
+ copyInteger(Downloads.Impl.COLUMN_FLAGS, values, filteredValues);
}
if (Constants.LOGVV) {
@@ -689,9 +692,18 @@ public final class DownloadProvider extends ContentProvider {
insertRequestHeaders(db, rowID, values);
notifyContentChanged(uri, match);
- // Always start service to handle notifications and/or scanning
- final Context context = getContext();
- context.startService(new Intent(context, DownloadService.class));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Helpers.scheduleJob(getContext(), rowID);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ if (values.getAsInteger(COLUMN_DESTINATION) == DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD
+ && values.getAsInteger(COLUMN_MEDIA_SCANNED) == 0) {
+ DownloadScanner.requestScanBlocking(getContext(), rowID, values.getAsString(_DATA),
+ values.getAsString(COLUMN_MIME_TYPE));
+ }
return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, rowID);
}
@@ -806,6 +818,7 @@ public final class DownloadProvider extends ContentProvider {
values.remove(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES);
values.remove(Downloads.Impl.COLUMN_ALLOW_ROAMING);
values.remove(Downloads.Impl.COLUMN_ALLOW_METERED);
+ values.remove(Downloads.Impl.COLUMN_FLAGS);
values.remove(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI);
values.remove(Downloads.Impl.COLUMN_MEDIA_SCANNED);
values.remove(Downloads.Impl.COLUMN_ALLOW_WRITE);
@@ -1053,14 +1066,7 @@ public final class DownloadProvider extends ContentProvider {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
- boolean startService = false;
-
- if (values.containsKey(Downloads.Impl.COLUMN_DELETED)) {
- if (values.getAsInteger(Downloads.Impl.COLUMN_DELETED) == 1) {
- // some rows are to be 'deleted'. need to start DownloadService.
- startService = true;
- }
- }
+ boolean updateSchedule = false;
ContentValues filteredValues;
if (Binder.getCallingPid() != Process.myPid()) {
@@ -1070,7 +1076,7 @@ public final class DownloadProvider extends ContentProvider {
Integer i = values.getAsInteger(Downloads.Impl.COLUMN_CONTROL);
if (i != null) {
filteredValues.put(Downloads.Impl.COLUMN_CONTROL, i);
- startService = true;
+ updateSchedule = true;
}
copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues);
@@ -1099,7 +1105,7 @@ public final class DownloadProvider extends ContentProvider {
boolean isUserBypassingSizeLimit =
values.containsKey(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT);
if (isRestart || isUserBypassingSizeLimit) {
- startService = true;
+ updateSchedule = true;
}
}
@@ -1109,12 +1115,27 @@ public final class DownloadProvider extends ContentProvider {
case MY_DOWNLOADS_ID:
case ALL_DOWNLOADS:
case ALL_DOWNLOADS_ID:
- SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
- if (filteredValues.size() > 0) {
- count = db.update(DB_TABLE, filteredValues, selection.getSelection(),
- selection.getParameters());
- } else {
+ if (filteredValues.size() == 0) {
count = 0;
+ break;
+ }
+
+ final SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
+ count = db.update(DB_TABLE, filteredValues, selection.getSelection(),
+ selection.getParameters());
+ if (updateSchedule) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ try (Cursor cursor = db.query(DB_TABLE, new String[] { _ID },
+ selection.getSelection(), selection.getParameters(),
+ null, null, null)) {
+ while (cursor.moveToNext()) {
+ Helpers.scheduleJob(getContext(), cursor.getInt(0));
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
break;
@@ -1124,10 +1145,6 @@ public final class DownloadProvider extends ContentProvider {
}
notifyContentChanged(uri, match);
- if (startService) {
- Context context = getContext();
- context.startService(new Intent(context, DownloadService.class));
- }
return count;
}
@@ -1176,7 +1193,8 @@ public final class DownloadProvider extends ContentProvider {
Helpers.validateSelection(where, sAppReadableColumnsSet);
}
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ final JobScheduler scheduler = getContext().getSystemService(JobScheduler.class);
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
int match = sURIMatcher.match(uri);
switch (match) {
@@ -1184,15 +1202,16 @@ public final class DownloadProvider extends ContentProvider {
case MY_DOWNLOADS_ID:
case ALL_DOWNLOADS:
case ALL_DOWNLOADS_ID:
- SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
+ final SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
deleteRequestHeaders(db, selection.getSelection(), selection.getParameters());
- final Cursor cursor = db.query(DB_TABLE, new String[] {
- Downloads.Impl._ID, Downloads.Impl._DATA
- }, selection.getSelection(), selection.getParameters(), null, null, null);
- try {
+ try (Cursor cursor = db.query(DB_TABLE, new String[] {
+ _ID, _DATA, COLUMN_MEDIAPROVIDER_URI
+ }, selection.getSelection(), selection.getParameters(), null, null, null)) {
while (cursor.moveToNext()) {
final long id = cursor.getLong(0);
+ scheduler.cancel((int) id);
+
DownloadStorageProvider.onDownloadProviderDelete(getContext(), id);
final String path = cursor.getString(1);
@@ -1207,9 +1226,18 @@ public final class DownloadProvider extends ContentProvider {
} catch (IOException ignored) {
}
}
+
+ final String mediaUri = cursor.getString(2);
+ if (!TextUtils.isEmpty(mediaUri)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ getContext().getContentResolver().delete(Uri.parse(mediaUri), null,
+ null);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
- } finally {
- IoUtils.closeQuietly(cursor);
}
count = db.delete(DB_TABLE, selection.getSelection(), selection.getParameters());
@@ -1270,7 +1298,13 @@ public final class DownloadProvider extends ContentProvider {
throw new FileNotFoundException("No filename found.");
}
- final File file = new File(path);
+ final File file;
+ try {
+ file = new File(path).getCanonicalFile();
+ } catch (IOException e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+
if (!Helpers.isFilenameValid(getContext(), file)) {
throw new FileNotFoundException("Invalid file: " + file);
}
@@ -1281,7 +1315,8 @@ public final class DownloadProvider extends ContentProvider {
} else {
try {
// When finished writing, update size and timestamp
- return ParcelFileDescriptor.open(file, pfdMode, mHandler, new OnCloseListener() {
+ return ParcelFileDescriptor.open(file, pfdMode, Helpers.getAsyncHandler(),
+ new OnCloseListener() {
@Override
public void onClose(IOException e) {
final ContentValues values = new ContentValues();
diff --git a/src/com/android/providers/downloads/DownloadReceiver.java b/src/com/android/providers/downloads/DownloadReceiver.java
index 28e2a673..a0dc6947 100644
--- a/src/com/android/providers/downloads/DownloadReceiver.java
+++ b/src/com/android/providers/downloads/DownloadReceiver.java
@@ -18,9 +18,16 @@ package com.android.providers.downloads;
import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
import static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION;
+
import static com.android.providers.downloads.Constants.TAG;
+import static com.android.providers.downloads.Helpers.getAsyncHandler;
+import static com.android.providers.downloads.Helpers.getDownloadNotifier;
+import static com.android.providers.downloads.Helpers.getInt;
+import static com.android.providers.downloads.Helpers.getString;
+import static com.android.providers.downloads.Helpers.getSystemFacade;
import android.app.DownloadManager;
+import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -28,58 +35,49 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.provider.Downloads;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.widget.Toast;
-import com.google.common.annotations.VisibleForTesting;
-
/**
* Receives system broadcasts (boot, network connectivity)
*/
public class DownloadReceiver extends BroadcastReceiver {
- private static Handler sAsyncHandler;
-
- static {
- final HandlerThread thread = new HandlerThread("DownloadReceiver");
- thread.start();
- sAsyncHandler = new Handler(thread.getLooper());
- }
+ /**
+ * Intent extra included with {@link Constants#ACTION_CANCEL} intents,
+ * indicating the IDs (as array of long) of the downloads that were
+ * canceled.
+ */
+ public static final String EXTRA_CANCELED_DOWNLOAD_IDS =
+ "com.android.providers.downloads.extra.CANCELED_DOWNLOAD_IDS";
- @VisibleForTesting
- SystemFacade mSystemFacade = null;
+ /**
+ * Intent extra included with {@link Constants#ACTION_CANCEL} intents,
+ * indicating the tag of the notification corresponding to the download(s)
+ * that were canceled; this notification must be canceled.
+ */
+ public static final String EXTRA_CANCELED_DOWNLOAD_NOTIFICATION_TAG =
+ "com.android.providers.downloads.extra.CANCELED_DOWNLOAD_NOTIFICATION_TAG";
@Override
public void onReceive(final Context context, final Intent intent) {
- if (mSystemFacade == null) {
- mSystemFacade = new RealSystemFacade(context);
- }
-
final String action = intent.getAction();
- if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- startService(context);
-
- } else if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
- startService(context);
-
- } else if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
- final ConnectivityManager connManager = (ConnectivityManager) context
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- final NetworkInfo info = connManager.getActiveNetworkInfo();
- if (info != null && info.isConnected()) {
- startService(context);
- }
-
+ if (Intent.ACTION_BOOT_COMPLETED.equals(action)
+ || Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
+ final PendingResult result = goAsync();
+ getAsyncHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ handleBootCompleted(context);
+ result.finish();
+ }
+ });
} else if (Intent.ACTION_UID_REMOVED.equals(action)) {
final PendingResult result = goAsync();
- sAsyncHandler.post(new Runnable() {
+ getAsyncHandler().post(new Runnable() {
@Override
public void run() {
handleUidRemoved(context, intent);
@@ -87,9 +85,6 @@ public class DownloadReceiver extends BroadcastReceiver {
}
});
- } else if (Constants.ACTION_RETRY.equals(action)) {
- startService(context);
-
} else if (Constants.ACTION_OPEN.equals(action)
|| Constants.ACTION_LIST.equals(action)
|| Constants.ACTION_HIDE.equals(action)) {
@@ -99,7 +94,7 @@ public class DownloadReceiver extends BroadcastReceiver {
// TODO: remove this once test is refactored
handleNotificationBroadcast(context, intent);
} else {
- sAsyncHandler.post(new Runnable() {
+ getAsyncHandler().post(new Runnable() {
@Override
public void run() {
handleNotificationBroadcast(context, intent);
@@ -107,7 +102,39 @@ public class DownloadReceiver extends BroadcastReceiver {
}
});
}
+ } else if (Constants.ACTION_CANCEL.equals(action)) {
+ long[] downloadIds = intent.getLongArrayExtra(
+ DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_IDS);
+ DownloadManager manager = (DownloadManager) context.getSystemService(
+ Context.DOWNLOAD_SERVICE);
+ manager.remove(downloadIds);
+
+ String notifTag = intent.getStringExtra(
+ DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_NOTIFICATION_TAG);
+ NotificationManager notifManager = (NotificationManager) context.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+ notifManager.cancel(notifTag, 0);
+ }
+ }
+
+ private void handleBootCompleted(Context context) {
+ // Show any relevant notifications for completed downloads
+ getDownloadNotifier(context).update();
+
+ // Schedule all downloads that are ready
+ final ContentResolver resolver = context.getContentResolver();
+ try (Cursor cursor = resolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, null, null,
+ null, null)) {
+ final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver, cursor);
+ final DownloadInfo info = new DownloadInfo(context);
+ while (cursor.moveToNext()) {
+ reader.updateFromDatabase(info);
+ Helpers.scheduleJob(context, info);
+ }
}
+
+ // Schedule idle pass to clean up orphaned files
+ DownloadIdleService.scheduleIdlePass(context);
}
private void handleUidRemoved(Context context, Intent intent) {
@@ -238,18 +265,6 @@ public class DownloadReceiver extends BroadcastReceiver {
}
}
- mSystemFacade.sendBroadcast(appIntent);
- }
-
- private static String getString(Cursor cursor, String col) {
- return cursor.getString(cursor.getColumnIndexOrThrow(col));
- }
-
- private static int getInt(Cursor cursor, String col) {
- return cursor.getInt(cursor.getColumnIndexOrThrow(col));
- }
-
- private void startService(Context context) {
- context.startService(new Intent(context, DownloadService.class));
+ getSystemFacade(context).sendBroadcast(appIntent);
}
}
diff --git a/src/com/android/providers/downloads/DownloadScanner.java b/src/com/android/providers/downloads/DownloadScanner.java
index ca795062..4a5ba87e 100644
--- a/src/com/android/providers/downloads/DownloadScanner.java
+++ b/src/com/android/providers/downloads/DownloadScanner.java
@@ -35,6 +35,8 @@ import com.android.internal.annotations.GuardedBy;
import com.google.common.collect.Maps;
import java.util.HashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Manages asynchronous scanning of completed downloads.
@@ -66,11 +68,30 @@ public class DownloadScanner implements MediaScannerConnectionClient {
@GuardedBy("mConnection")
private HashMap<String, ScanRequest> mPending = Maps.newHashMap();
+ private CountDownLatch mLatch;
+
public DownloadScanner(Context context) {
mContext = context;
mConnection = new MediaScannerConnection(context, this);
}
+ public static void requestScanBlocking(Context context, DownloadInfo info) {
+ requestScanBlocking(context, info.mId, info.mFileName, info.mMimeType);
+ }
+
+ public static void requestScanBlocking(Context context, long id, String path, String mimeType) {
+ final DownloadScanner scanner = new DownloadScanner(context);
+ scanner.mLatch = new CountDownLatch(1);
+ scanner.requestScan(new ScanRequest(id, path, mimeType));
+ try {
+ scanner.mLatch.await(SCAN_TIMEOUT, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } finally {
+ scanner.shutdown();
+ }
+ }
+
/**
* Check if requested scans are still pending. Scans may timeout after an
* internal duration.
@@ -98,10 +119,9 @@ public class DownloadScanner implements MediaScannerConnectionClient {
*
* @see #hasPendingScans()
*/
- public void requestScan(DownloadInfo info) {
- if (LOGV) Log.v(TAG, "requestScan() for " + info.mFileName);
+ public void requestScan(ScanRequest req) {
+ if (LOGV) Log.v(TAG, "requestScan() for " + req.path);
synchronized (mConnection) {
- final ScanRequest req = new ScanRequest(info.mId, info.mFileName, info.mMimeType);
mPending.put(req.path, req);
if (mConnection.isConnected()) {
@@ -153,5 +173,9 @@ public class DownloadScanner implements MediaScannerConnectionClient {
// so clean up now-orphaned media entry.
resolver.delete(uri, null, null);
}
+
+ if (mLatch != null) {
+ mLatch.countDown();
+ }
}
}
diff --git a/src/com/android/providers/downloads/DownloadService.java b/src/com/android/providers/downloads/DownloadService.java
deleted file mode 100644
index b0b73297..00000000
--- a/src/com/android/providers/downloads/DownloadService.java
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * 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 static android.text.format.DateUtils.MINUTE_IN_MILLIS;
-import static com.android.providers.downloads.Constants.TAG;
-
-import android.app.AlarmManager;
-import android.app.DownloadManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.app.job.JobInfo;
-import android.app.job.JobScheduler;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Process;
-import android.provider.Downloads;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.IndentingPrintWriter;
-import com.google.android.collect.Maps;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Performs background downloads as requested by applications that use
- * {@link DownloadManager}. Multiple start commands can be issued at this
- * service, and it will continue running until no downloads are being actively
- * processed. It may schedule alarms to resume downloads in future.
- * <p>
- * Any database updates important enough to initiate tasks should always be
- * delivered through {@link Context#startService(Intent)}.
- */
-public class DownloadService extends Service {
- // TODO: migrate WakeLock from individual DownloadThreads out into
- // DownloadReceiver to protect our entire workflow.
-
- private static final boolean DEBUG_LIFECYCLE = false;
-
- @VisibleForTesting
- SystemFacade mSystemFacade;
-
- private AlarmManager mAlarmManager;
-
- /** Observer to get notified when the content observer's data changes */
- private DownloadManagerContentObserver mObserver;
-
- /** Class to handle Notification Manager updates */
- private DownloadNotifier mNotifier;
-
- /** Scheduling of the periodic cleanup job */
- private JobInfo mCleanupJob;
-
- private static final int CLEANUP_JOB_ID = 1;
- private static final long CLEANUP_JOB_PERIOD = 1000 * 60 * 60 * 24; // one day
- private static ComponentName sCleanupServiceName = new ComponentName(
- DownloadIdleService.class.getPackage().getName(),
- DownloadIdleService.class.getName());
-
- /**
- * The Service's view of the list of downloads, mapping download IDs to the corresponding info
- * object. This is kept independently from the content provider, and the Service only initiates
- * downloads based on this data, so that it can deal with situation where the data in the
- * content provider changes or disappears.
- */
- @GuardedBy("mDownloads")
- private final Map<Long, DownloadInfo> mDownloads = Maps.newHashMap();
-
- private final ExecutorService mExecutor = buildDownloadExecutor();
-
- private static ExecutorService buildDownloadExecutor() {
- final int maxConcurrent = Resources.getSystem().getInteger(
- com.android.internal.R.integer.config_MaxConcurrentDownloadsAllowed);
-
- // Create a bounded thread pool for executing downloads; it creates
- // threads as needed (up to maximum) and reclaims them when finished.
- final ThreadPoolExecutor executor = new ThreadPoolExecutor(
- maxConcurrent, maxConcurrent, 10, TimeUnit.SECONDS,
- new LinkedBlockingQueue<Runnable>()) {
- @Override
- protected void afterExecute(Runnable r, Throwable t) {
- super.afterExecute(r, t);
-
- if (t == null && r instanceof Future<?>) {
- try {
- ((Future<?>) r).get();
- } catch (CancellationException ce) {
- t = ce;
- } catch (ExecutionException ee) {
- t = ee.getCause();
- } catch (InterruptedException ie) {
- Thread.currentThread().interrupt();
- }
- }
-
- if (t != null) {
- Log.w(TAG, "Uncaught exception", t);
- }
- }
- };
- executor.allowCoreThreadTimeOut(true);
- return executor;
- }
-
- private DownloadScanner mScanner;
-
- private HandlerThread mUpdateThread;
- private Handler mUpdateHandler;
-
- private volatile int mLastStartId;
-
- /**
- * Receives notifications when the data in the content provider changes
- */
- private class DownloadManagerContentObserver extends ContentObserver {
- public DownloadManagerContentObserver() {
- super(new Handler());
- }
-
- @Override
- public void onChange(final boolean selfChange) {
- enqueueUpdate();
- }
- }
-
- /**
- * Returns an IBinder instance when someone wants to connect to this
- * service. Binding to this service is not allowed.
- *
- * @throws UnsupportedOperationException
- */
- @Override
- public IBinder onBind(Intent i) {
- throw new UnsupportedOperationException("Cannot bind to Download Manager Service");
- }
-
- /**
- * Initializes the service when it is first created
- */
- @Override
- public void onCreate() {
- super.onCreate();
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Service onCreate");
- }
-
- if (mSystemFacade == null) {
- mSystemFacade = new RealSystemFacade(this);
- }
-
- mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
-
- mUpdateThread = new HandlerThread(TAG + "-UpdateThread");
- mUpdateThread.start();
- mUpdateHandler = new Handler(mUpdateThread.getLooper(), mUpdateCallback);
-
- mScanner = new DownloadScanner(this);
-
- mNotifier = new DownloadNotifier(this);
- mNotifier.cancelAll();
-
- mObserver = new DownloadManagerContentObserver();
- getContentResolver().registerContentObserver(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
- true, mObserver);
-
- JobScheduler js = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
- if (needToScheduleCleanup(js)) {
- final JobInfo job = new JobInfo.Builder(CLEANUP_JOB_ID, sCleanupServiceName)
- .setPeriodic(CLEANUP_JOB_PERIOD)
- .setRequiresCharging(true)
- .setRequiresDeviceIdle(true)
- .build();
- js.schedule(job);
- }
- }
-
- private boolean needToScheduleCleanup(JobScheduler js) {
- List<JobInfo> myJobs = js.getAllPendingJobs();
- if (myJobs != null) {
- final int N = myJobs.size();
- for (int i = 0; i < N; i++) {
- if (myJobs.get(i).getId() == CLEANUP_JOB_ID) {
- // It's already been (persistently) scheduled; no need to do it again
- return false;
- }
- }
- }
- return true;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- int returnValue = super.onStartCommand(intent, flags, startId);
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Service onStart");
- }
- mLastStartId = startId;
- enqueueUpdate();
- return returnValue;
- }
-
- @Override
- public void onDestroy() {
- getContentResolver().unregisterContentObserver(mObserver);
- mScanner.shutdown();
- mUpdateThread.quit();
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "Service onDestroy");
- }
- super.onDestroy();
- }
-
- /**
- * Enqueue an {@link #updateLocked()} pass to occur in future.
- */
- public void enqueueUpdate() {
- if (mUpdateHandler != null) {
- mUpdateHandler.removeMessages(MSG_UPDATE);
- mUpdateHandler.obtainMessage(MSG_UPDATE, mLastStartId, -1).sendToTarget();
- }
- }
-
- /**
- * Enqueue an {@link #updateLocked()} pass to occur after delay, usually to
- * catch any finished operations that didn't trigger an update pass.
- */
- private void enqueueFinalUpdate() {
- mUpdateHandler.removeMessages(MSG_FINAL_UPDATE);
- mUpdateHandler.sendMessageDelayed(
- mUpdateHandler.obtainMessage(MSG_FINAL_UPDATE, mLastStartId, -1),
- 5 * MINUTE_IN_MILLIS);
- }
-
- private static final int MSG_UPDATE = 1;
- private static final int MSG_FINAL_UPDATE = 2;
-
- private Handler.Callback mUpdateCallback = new Handler.Callback() {
- @Override
- public boolean handleMessage(Message msg) {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-
- final int startId = msg.arg1;
- if (DEBUG_LIFECYCLE) Log.v(TAG, "Updating for startId " + startId);
-
- // Since database is current source of truth, our "active" status
- // depends on database state. We always get one final update pass
- // once the real actions have finished and persisted their state.
-
- // TODO: switch to asking real tasks to derive active state
- // TODO: handle media scanner timeouts
-
- final boolean isActive;
- synchronized (mDownloads) {
- isActive = updateLocked();
- }
-
- if (msg.what == MSG_FINAL_UPDATE) {
- // Dump thread stacks belonging to pool
- for (Map.Entry<Thread, StackTraceElement[]> entry :
- Thread.getAllStackTraces().entrySet()) {
- if (entry.getKey().getName().startsWith("pool")) {
- Log.d(TAG, entry.getKey() + ": " + Arrays.toString(entry.getValue()));
- }
- }
-
- // Dump speed and update details
- mNotifier.dumpSpeeds();
-
- Log.wtf(TAG, "Final update pass triggered, isActive=" + isActive
- + "; someone didn't update correctly.");
- }
-
- if (isActive) {
- // Still doing useful work, keep service alive. These active
- // tasks will trigger another update pass when they're finished.
-
- // Enqueue delayed update pass to catch finished operations that
- // didn't trigger an update pass; these are bugs.
- enqueueFinalUpdate();
-
- } else {
- // No active tasks, and any pending update messages can be
- // ignored, since any updates important enough to initiate tasks
- // will always be delivered with a new startId.
-
- if (stopSelfResult(startId)) {
- if (DEBUG_LIFECYCLE) Log.v(TAG, "Nothing left; stopped");
- getContentResolver().unregisterContentObserver(mObserver);
- mScanner.shutdown();
- mUpdateThread.quit();
- }
- }
-
- return true;
- }
- };
-
- /**
- * Update {@link #mDownloads} to match {@link DownloadProvider} state.
- * Depending on current download state it may enqueue {@link DownloadThread}
- * instances, request {@link DownloadScanner} scans, update user-visible
- * notifications, and/or schedule future actions with {@link AlarmManager}.
- * <p>
- * Should only be called from {@link #mUpdateThread} as after being
- * requested through {@link #enqueueUpdate()}.
- *
- * @return If there are active tasks being processed, as of the database
- * snapshot taken in this update.
- */
- private boolean updateLocked() {
- final long now = mSystemFacade.currentTimeMillis();
-
- boolean isActive = false;
- long nextActionMillis = Long.MAX_VALUE;
-
- final Set<Long> staleIds = Sets.newHashSet(mDownloads.keySet());
-
- final ContentResolver resolver = getContentResolver();
- final Cursor cursor = resolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
- null, null, null, null);
- try {
- final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver, cursor);
- final int idColumn = cursor.getColumnIndexOrThrow(Downloads.Impl._ID);
- while (cursor.moveToNext()) {
- final long id = cursor.getLong(idColumn);
- staleIds.remove(id);
-
- DownloadInfo info = mDownloads.get(id);
- if (info != null) {
- updateDownload(reader, info, now);
- } else {
- info = insertDownloadLocked(reader, now);
- }
-
- if (info.mDeleted) {
- // Delete download if requested, but only after cleaning up
- if (!TextUtils.isEmpty(info.mMediaProviderUri)) {
- resolver.delete(Uri.parse(info.mMediaProviderUri), null, null);
- }
-
- deleteFileIfExists(info.mFileName);
- resolver.delete(info.getAllDownloadsUri(), null, null);
-
- } else {
- // Kick off download task if ready
- final boolean activeDownload = info.startDownloadIfReady(mExecutor);
-
- // Kick off media scan if completed
- final boolean activeScan = info.startScanIfReady(mScanner);
-
- if (DEBUG_LIFECYCLE && (activeDownload || activeScan)) {
- Log.v(TAG, "Download " + info.mId + ": activeDownload=" + activeDownload
- + ", activeScan=" + activeScan);
- }
-
- isActive |= activeDownload;
- isActive |= activeScan;
- }
-
- // Keep track of nearest next action
- nextActionMillis = Math.min(info.nextActionMillis(now), nextActionMillis);
- }
- } finally {
- cursor.close();
- }
-
- // Clean up stale downloads that disappeared
- for (Long id : staleIds) {
- deleteDownloadLocked(id);
- }
-
- // Update notifications visible to user
- mNotifier.updateWith(mDownloads.values());
-
- // Set alarm when next action is in future. It's okay if the service
- // continues to run in meantime, since it will kick off an update pass.
- if (nextActionMillis > 0 && nextActionMillis < Long.MAX_VALUE) {
- if (Constants.LOGV) {
- Log.v(TAG, "scheduling start in " + nextActionMillis + "ms");
- }
-
- final Intent intent = new Intent(Constants.ACTION_RETRY);
- intent.setClass(this, DownloadReceiver.class);
- mAlarmManager.set(AlarmManager.RTC_WAKEUP, now + nextActionMillis,
- PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_ONE_SHOT));
- }
-
- return isActive;
- }
-
- /**
- * Keeps a local copy of the info about a download, and initiates the
- * download if appropriate.
- */
- private DownloadInfo insertDownloadLocked(DownloadInfo.Reader reader, long now) {
- final DownloadInfo info = reader.newDownloadInfo(this, mSystemFacade, mNotifier);
- mDownloads.put(info.mId, info);
-
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "processing inserted download " + info.mId);
- }
-
- return info;
- }
-
- /**
- * Updates the local copy of the info about a download.
- */
- private void updateDownload(DownloadInfo.Reader reader, DownloadInfo info, long now) {
- reader.updateFromDatabase(info);
- if (Constants.LOGVV) {
- Log.v(Constants.TAG, "processing updated download " + info.mId +
- ", status: " + info.mStatus);
- }
- }
-
- /**
- * Removes the local copy of the info about a download.
- */
- private void deleteDownloadLocked(long id) {
- DownloadInfo info = mDownloads.get(id);
- if (info.mStatus == Downloads.Impl.STATUS_RUNNING) {
- info.mStatus = Downloads.Impl.STATUS_CANCELED;
- }
- if (info.mDestination != Downloads.Impl.DESTINATION_EXTERNAL && info.mFileName != null) {
- if (Constants.LOGVV) {
- Log.d(TAG, "deleteDownloadLocked() deleting " + info.mFileName);
- }
- deleteFileIfExists(info.mFileName);
- }
- mDownloads.remove(info.mId);
- }
-
- private void deleteFileIfExists(String path) {
- if (!TextUtils.isEmpty(path)) {
- if (Constants.LOGVV) {
- Log.d(TAG, "deleteFileIfExists() deleting " + path);
- }
- final File file = new File(path);
- if (file.exists() && !file.delete()) {
- Log.w(TAG, "file: '" + path + "' couldn't be deleted");
- }
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- synchronized (mDownloads) {
- final List<Long> ids = Lists.newArrayList(mDownloads.keySet());
- Collections.sort(ids);
- for (Long id : ids) {
- final DownloadInfo info = mDownloads.get(id);
- info.dump(pw);
- }
- }
- }
-}
diff --git a/src/com/android/providers/downloads/DownloadStorageProvider.java b/src/com/android/providers/downloads/DownloadStorageProvider.java
index 1b5dc844..e0bb7cd1 100644
--- a/src/com/android/providers/downloads/DownloadStorageProvider.java
+++ b/src/com/android/providers/downloads/DownloadStorageProvider.java
@@ -35,6 +35,7 @@ import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
+import android.support.provider.DocumentArchiveHelper;
import android.text.TextUtils;
import android.webkit.MimeTypeMap;
@@ -65,11 +66,14 @@ public class DownloadStorageProvider extends DocumentsProvider {
};
private DownloadManager mDm;
+ private DocumentArchiveHelper mArchiveHelper;
@Override
public boolean onCreate() {
mDm = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
mDm.setAccessAllDownloads(true);
+ mDm.setAccessFilename(true);
+ mArchiveHelper = new DocumentArchiveHelper(this, ':');
return true;
}
@@ -151,7 +155,30 @@ public class DownloadStorageProvider extends DocumentsProvider {
}
@Override
+ public String renameDocument(String documentId, String displayName)
+ throws FileNotFoundException {
+ displayName = FileUtils.buildValidFatFilename(displayName);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final long id = Long.parseLong(documentId);
+
+ if (!mDm.rename(getContext(), id, displayName)) {
+ throw new IllegalStateException(
+ "Failed to rename to " + displayName + " in downloadsManager");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return null;
+ }
+
+ @Override
public Cursor queryDocument(String docId, String[] projection) throws FileNotFoundException {
+ if (mArchiveHelper.isArchivedDocument(docId)) {
+ return mArchiveHelper.queryDocument(docId, projection);
+ }
+
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
if (DOC_ID_ROOT.equals(docId)) {
@@ -164,6 +191,8 @@ public class DownloadStorageProvider extends DocumentsProvider {
cursor = mDm.query(new Query().setFilterById(Long.parseLong(docId)));
copyNotificationUri(result, cursor);
if (cursor.moveToFirst()) {
+ // We don't know if this queryDocument() call is from Downloads (manage)
+ // or Files. Safely assume it's Files.
includeDownloadFromCursor(result, cursor);
}
} finally {
@@ -177,6 +206,11 @@ public class DownloadStorageProvider extends DocumentsProvider {
@Override
public Cursor queryChildDocuments(String docId, String[] projection, String sortOrder)
throws FileNotFoundException {
+ if (mArchiveHelper.isArchivedDocument(docId) ||
+ mArchiveHelper.isSupportedArchiveType(getDocumentType(docId))) {
+ return mArchiveHelper.queryChildDocuments(docId, projection, sortOrder);
+ }
+
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
// Delegate to real provider
@@ -200,6 +234,10 @@ public class DownloadStorageProvider extends DocumentsProvider {
public Cursor queryChildDocumentsForManage(
String parentDocumentId, String[] projection, String sortOrder)
throws FileNotFoundException {
+ if (mArchiveHelper.isArchivedDocument(parentDocumentId)) {
+ return mArchiveHelper.queryDocument(parentDocumentId, projection);
+ }
+
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
// Delegate to real provider
@@ -256,6 +294,10 @@ public class DownloadStorageProvider extends DocumentsProvider {
@Override
public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
throws FileNotFoundException {
+ if (mArchiveHelper.isArchivedDocument(docId)) {
+ return mArchiveHelper.openDocument(docId, mode, signal);
+ }
+
// Delegate to real provider
final long token = Binder.clearCallingIdentity();
try {
@@ -303,10 +345,12 @@ public class DownloadStorageProvider extends DocumentsProvider {
size = null;
}
+ int extraFlags = Document.FLAG_PARTIAL;
final int status = cursor.getInt(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
switch (status) {
case DownloadManager.STATUS_SUCCESSFUL:
+ extraFlags = Document.FLAG_SUPPORTS_RENAME; // only successful is non-partial
break;
case DownloadManager.STATUS_PAUSED:
summary = getContext().getString(R.string.download_queued);
@@ -331,11 +375,15 @@ public class DownloadStorageProvider extends DocumentsProvider {
break;
}
- int flags = Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE;
- if (mimeType != null && mimeType.startsWith("image/")) {
+ int flags = Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE | extraFlags;
+ if (mimeType.startsWith("image/")) {
flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
}
+ if (mArchiveHelper.isSupportedArchiveType(mimeType)) {
+ flags |= Document.FLAG_ARCHIVE;
+ }
+
final long lastModified = cursor.getLong(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP));
@@ -345,8 +393,18 @@ public class DownloadStorageProvider extends DocumentsProvider {
row.add(Document.COLUMN_SUMMARY, summary);
row.add(Document.COLUMN_SIZE, size);
row.add(Document.COLUMN_MIME_TYPE, mimeType);
- row.add(Document.COLUMN_LAST_MODIFIED, lastModified);
row.add(Document.COLUMN_FLAGS, flags);
+ // Incomplete downloads get a null timestamp. This prevents thrashy UI when a bunch of
+ // active downloads get sorted by mod time.
+ if (status != DownloadManager.STATUS_RUNNING) {
+ row.add(Document.COLUMN_LAST_MODIFIED, lastModified);
+ }
+
+ final String localFilePath = cursor.getString(
+ cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME));
+ if (localFilePath != null) {
+ row.add(DocumentArchiveHelper.COLUMN_LOCAL_FILE_PATH, localFilePath);
+ }
}
/**
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index 325b4eee..40194038 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -16,11 +16,18 @@
package com.android.providers.downloads;
+import static android.provider.Downloads.Impl.COLUMN_CONTROL;
+import static android.provider.Downloads.Impl.COLUMN_DELETED;
+import static android.provider.Downloads.Impl.COLUMN_STATUS;
+import static android.provider.Downloads.Impl.CONTROL_PAUSED;
import static android.provider.Downloads.Impl.STATUS_BAD_REQUEST;
import static android.provider.Downloads.Impl.STATUS_CANCELED;
import static android.provider.Downloads.Impl.STATUS_CANNOT_RESUME;
import static android.provider.Downloads.Impl.STATUS_FILE_ERROR;
import static android.provider.Downloads.Impl.STATUS_HTTP_DATA_ERROR;
+import static android.provider.Downloads.Impl.STATUS_PAUSED_BY_APP;
+import static android.provider.Downloads.Impl.STATUS_QUEUED_FOR_WIFI;
+import static android.provider.Downloads.Impl.STATUS_RUNNING;
import static android.provider.Downloads.Impl.STATUS_SUCCESS;
import static android.provider.Downloads.Impl.STATUS_TOO_MANY_REDIRECTS;
import static android.provider.Downloads.Impl.STATUS_UNHANDLED_HTTP_CODE;
@@ -28,7 +35,9 @@ import static android.provider.Downloads.Impl.STATUS_UNKNOWN_ERROR;
import static android.provider.Downloads.Impl.STATUS_WAITING_FOR_NETWORK;
import static android.provider.Downloads.Impl.STATUS_WAITING_TO_RETRY;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+
import static com.android.providers.downloads.Constants.TAG;
+
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
import static java.net.HttpURLConnection.HTTP_MOVED_PERM;
import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
@@ -38,6 +47,8 @@ import static java.net.HttpURLConnection.HTTP_PRECON_FAILED;
import static java.net.HttpURLConnection.HTTP_SEE_OTHER;
import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -45,24 +56,22 @@ import android.drm.DrmManagerClient;
import android.drm.DrmOutputStream;
import android.net.ConnectivityManager;
import android.net.INetworkPolicyListener;
+import android.net.Network;
import android.net.NetworkInfo;
import android.net.NetworkPolicyManager;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
import android.os.Process;
import android.os.SystemClock;
-import android.os.WorkSource;
import android.provider.Downloads;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Pair;
-import com.android.providers.downloads.DownloadInfo.NetworkState;
-
import libcore.io.IoUtils;
import java.io.File;
@@ -76,6 +85,10 @@ import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;
+import java.security.GeneralSecurityException;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
/**
* Task which executes a given {@link DownloadInfo}: making network requests,
@@ -88,7 +101,7 @@ import java.net.URLConnection;
* Failed network requests are retried several times before giving up. Local
* disk errors fail immediately and are not retried.
*/
-public class DownloadThread implements Runnable {
+public class DownloadThread extends Thread {
// TODO: bind each download to a specific network interface to avoid state
// checking races once we have ConnectivityManager API
@@ -103,6 +116,10 @@ public class DownloadThread implements Runnable {
private final Context mContext;
private final SystemFacade mSystemFacade;
private final DownloadNotifier mNotifier;
+ private final NetworkPolicyManager mNetworkPolicy;
+
+ private final DownloadJobService mJobService;
+ private final JobParameters mParams;
private final long mId;
@@ -133,6 +150,14 @@ public class DownloadThread implements Runnable {
public String mErrorMsg;
+ private static final String NOT_CANCELED = COLUMN_STATUS + " != '" + STATUS_CANCELED + "'";
+ private static final String NOT_DELETED = COLUMN_DELETED + " == '0'";
+ private static final String NOT_PAUSED = "(" + COLUMN_CONTROL + " IS NULL OR "
+ + COLUMN_CONTROL + " != '" + CONTROL_PAUSED + "')";
+
+ private static final String SELECTION_VALID = NOT_CANCELED + " AND " + NOT_DELETED + " AND "
+ + NOT_PAUSED;
+
public DownloadInfoDelta(DownloadInfo info) {
mUri = info.mUri;
mFileName = info.mFileName;
@@ -178,8 +203,12 @@ public class DownloadThread implements Runnable {
*/
public void writeToDatabaseOrThrow() throws StopRequestException {
if (mContext.getContentResolver().update(mInfo.getAllDownloadsUri(),
- buildContentValues(), Downloads.Impl.COLUMN_DELETED + " == '0'", null) == 0) {
- throw new StopRequestException(STATUS_CANCELED, "Download deleted or missing!");
+ buildContentValues(), SELECTION_VALID, null) == 0) {
+ if (mInfo.queryDownloadControl() == CONTROL_PAUSED) {
+ throw new StopRequestException(STATUS_PAUSED_BY_APP, "Download paused!");
+ } else {
+ throw new StopRequestException(STATUS_CANCELED, "Download deleted or missing!");
+ }
}
}
}
@@ -196,6 +225,9 @@ public class DownloadThread implements Runnable {
private long mLastUpdateBytes = 0;
private long mLastUpdateTime = 0;
+ private boolean mIgnoreBlocked;
+ private Network mNetwork;
+
private int mNetworkType = ConnectivityManager.TYPE_NONE;
/** Historical bytes/second speed of this download. */
@@ -205,11 +237,17 @@ public class DownloadThread implements Runnable {
/** Bytes transferred since current sample started. */
private long mSpeedSampleBytes;
- public DownloadThread(Context context, SystemFacade systemFacade, DownloadNotifier notifier,
- DownloadInfo info) {
- mContext = context;
- mSystemFacade = systemFacade;
- mNotifier = notifier;
+ /** Flag indicating that thread must be halted */
+ private volatile boolean mShutdownRequested;
+
+ public DownloadThread(DownloadJobService service, JobParameters params, DownloadInfo info) {
+ mContext = service;
+ mSystemFacade = Helpers.getSystemFacade(mContext);
+ mNotifier = Helpers.getDownloadNotifier(mContext);
+ mNetworkPolicy = mContext.getSystemService(NetworkPolicyManager.class);
+
+ mJobService = service;
+ mParams = params;
mId = info.mId;
mInfo = info;
@@ -222,29 +260,38 @@ public class DownloadThread implements Runnable {
// Skip when download already marked as finished; this download was
// probably started again while racing with UpdateThread.
- if (DownloadInfo.queryDownloadStatus(mContext.getContentResolver(), mId)
- == Downloads.Impl.STATUS_SUCCESS) {
+ if (mInfo.queryDownloadStatus() == Downloads.Impl.STATUS_SUCCESS) {
logDebug("Already finished; skipping");
return;
}
- final NetworkPolicyManager netPolicy = NetworkPolicyManager.from(mContext);
- PowerManager.WakeLock wakeLock = null;
- final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-
try {
- wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);
- wakeLock.setWorkSource(new WorkSource(mInfo.mUid));
- wakeLock.acquire();
-
// while performing download, register for rules updates
- netPolicy.registerListener(mPolicyListener);
+ mNetworkPolicy.registerListener(mPolicyListener);
logDebug("Starting");
+ mInfoDelta.mStatus = STATUS_RUNNING;
+ mInfoDelta.writeToDatabase();
+
+ // If we're showing a foreground notification for the requesting
+ // app, the download isn't affected by the blocked status of the
+ // requesting app
+ mIgnoreBlocked = mInfo.isVisible();
+
+ // Use the caller's default network to make this connection, since
+ // they might be subject to restrictions that we shouldn't let them
+ // circumvent
+ mNetwork = mSystemFacade.getActiveNetwork(mInfo.mUid, mIgnoreBlocked);
+ if (mNetwork == null) {
+ throw new StopRequestException(STATUS_WAITING_FOR_NETWORK,
+ "No network associated with requesting UID");
+ }
+
// Remember which network this download started on; used to
// determine if errors were due to network changes.
- final NetworkInfo info = mSystemFacade.getActiveNetworkInfo(mInfo.mUid);
+ final NetworkInfo info = mSystemFacade.getNetworkInfo(mNetwork, mInfo.mUid,
+ mIgnoreBlocked);
if (info != null) {
mNetworkType = info.getType();
}
@@ -287,7 +334,8 @@ public class DownloadThread implements Runnable {
}
if (mInfoDelta.mNumFailed < Constants.MAX_RETRIES) {
- final NetworkInfo info = mSystemFacade.getActiveNetworkInfo(mInfo.mUid);
+ final NetworkInfo info = mSystemFacade.getNetworkInfo(mNetwork, mInfo.mUid,
+ mIgnoreBlocked);
if (info != null && info.getType() == mNetworkType && info.isConnected()) {
// Underlying network is still intact, use normal backoff
mInfoDelta.mStatus = STATUS_WAITING_TO_RETRY;
@@ -305,6 +353,13 @@ public class DownloadThread implements Runnable {
}
}
+ // If we're waiting for a network that must be unmetered, our status
+ // is actually queued so we show relevant notifications
+ if (mInfoDelta.mStatus == STATUS_WAITING_FOR_NETWORK
+ && !mInfo.isMeteredAllowed(mInfoDelta.mTotalBytes)) {
+ mInfoDelta.mStatus = STATUS_QUEUED_FOR_WIFI;
+ }
+
} catch (Throwable t) {
mInfoDelta.mStatus = STATUS_UNKNOWN_ERROR;
mInfoDelta.mErrorMsg = t.toString();
@@ -320,20 +375,29 @@ public class DownloadThread implements Runnable {
mInfoDelta.writeToDatabase();
- if (Downloads.Impl.isStatusCompleted(mInfoDelta.mStatus)) {
- mInfo.sendIntentIfRequested();
- }
-
TrafficStats.clearThreadStatsTag();
TrafficStats.clearThreadStatsUid();
- netPolicy.unregisterListener(mPolicyListener);
+ mNetworkPolicy.unregisterListener(mPolicyListener);
+ }
- if (wakeLock != null) {
- wakeLock.release();
- wakeLock = null;
+ if (Downloads.Impl.isStatusCompleted(mInfoDelta.mStatus)) {
+ mInfo.sendIntentIfRequested();
+ if (mInfo.shouldScanFile(mInfoDelta.mStatus)) {
+ DownloadScanner.requestScanBlocking(mContext, mInfo.mId, mInfoDelta.mFileName,
+ mInfoDelta.mMimeType);
}
+ } else if (mInfoDelta.mStatus == STATUS_WAITING_TO_RETRY
+ || mInfoDelta.mStatus == STATUS_WAITING_FOR_NETWORK
+ || mInfoDelta.mStatus == STATUS_QUEUED_FOR_WIFI) {
+ Helpers.scheduleJob(mContext, DownloadInfo.queryDownloadInfo(mContext, mId));
}
+
+ mJobService.jobFinishedInternal(mParams, false);
+ }
+
+ public void requestShutdown() {
+ mShutdownRequested = true;
}
/**
@@ -352,6 +416,13 @@ public class DownloadThread implements Runnable {
}
boolean cleartextTrafficPermitted = mSystemFacade.isCleartextTrafficPermitted(mInfo.mUid);
+ SSLContext appContext;
+ try {
+ appContext = mSystemFacade.getSSLContextForPackage(mContext, mInfo.mPackage);
+ } catch (GeneralSecurityException e) {
+ // This should never happen.
+ throw new StopRequestException(STATUS_UNKNOWN_ERROR, "Unable to create SSLContext.");
+ }
int redirectionCount = 0;
while (redirectionCount++ < Constants.MAX_REDIRECTS) {
// Enforce the cleartext traffic opt-out for the UID. This cannot be enforced earlier
@@ -366,11 +437,18 @@ public class DownloadThread implements Runnable {
// response with body.
HttpURLConnection conn = null;
try {
+ // Check that the caller is allowed to make network connections. If so, make one on
+ // their behalf to open the url.
checkConnectivity();
- conn = (HttpURLConnection) url.openConnection();
+ conn = (HttpURLConnection) mNetwork.openConnection(url);
conn.setInstanceFollowRedirects(false);
conn.setConnectTimeout(DEFAULT_TIMEOUT);
conn.setReadTimeout(DEFAULT_TIMEOUT);
+ // If this is going over HTTPS configure the trust to be the same as the calling
+ // package.
+ if (conn instanceof HttpsURLConnection) {
+ ((HttpsURLConnection)conn).setSSLSocketFactory(appContext.getSocketFactory());
+ }
addRequestHeaders(conn, resuming);
@@ -530,7 +608,7 @@ public class DownloadThread implements Runnable {
} finally {
if (drmClient != null) {
- drmClient.release();
+ drmClient.close();
}
IoUtils.closeQuietly(in);
@@ -553,7 +631,12 @@ public class DownloadThread implements Runnable {
throws StopRequestException {
final byte buffer[] = new byte[Constants.BUFFER_SIZE];
while (true) {
- checkPausedOrCanceled();
+ if (mPolicyDirty) checkConnectivity();
+
+ if (mShutdownRequested) {
+ throw new StopRequestException(STATUS_HTTP_DATA_ERROR,
+ "Local halt requested; job probably timed out");
+ }
int len = -1;
try {
@@ -624,12 +707,6 @@ public class DownloadThread implements Runnable {
} else if (Downloads.Impl.isStatusSuccess(mInfoDelta.mStatus)) {
// When success, open access if local file
if (mInfoDelta.mFileName != null) {
- try {
- // TODO: remove this once PackageInstaller works with content://
- Os.chmod(mInfoDelta.mFileName, 0644);
- } catch (ErrnoException ignored) {
- }
-
if (mInfo.mDestination != Downloads.Impl.DESTINATION_FILE_URI) {
try {
// Move into final resting place, if needed
@@ -659,38 +736,16 @@ public class DownloadThread implements Runnable {
// checking connectivity will apply current policy
mPolicyDirty = false;
- final NetworkState networkUsable = mInfo.checkCanUseNetwork(mInfoDelta.mTotalBytes);
- if (networkUsable != NetworkState.OK) {
- int status = Downloads.Impl.STATUS_WAITING_FOR_NETWORK;
- if (networkUsable == NetworkState.UNUSABLE_DUE_TO_SIZE) {
- status = Downloads.Impl.STATUS_QUEUED_FOR_WIFI;
- mInfo.notifyPauseDueToSize(true);
- } else if (networkUsable == NetworkState.RECOMMENDED_UNUSABLE_DUE_TO_SIZE) {
- status = Downloads.Impl.STATUS_QUEUED_FOR_WIFI;
- mInfo.notifyPauseDueToSize(false);
- }
- throw new StopRequestException(status, networkUsable.name());
+ final NetworkInfo info = mSystemFacade.getNetworkInfo(mNetwork, mInfo.mUid,
+ mIgnoreBlocked);
+ if (info == null || !info.isConnected()) {
+ throw new StopRequestException(STATUS_WAITING_FOR_NETWORK, "Network is disconnected");
}
- }
-
- /**
- * Check if the download has been paused or canceled, stopping the request
- * appropriately if it has been.
- */
- private void checkPausedOrCanceled() throws StopRequestException {
- synchronized (mInfo) {
- if (mInfo.mControl == Downloads.Impl.CONTROL_PAUSED) {
- throw new StopRequestException(
- Downloads.Impl.STATUS_PAUSED_BY_APP, "download paused by owner");
- }
- if (mInfo.mStatus == Downloads.Impl.STATUS_CANCELED || mInfo.mDeleted) {
- throw new StopRequestException(Downloads.Impl.STATUS_CANCELED, "download canceled");
- }
+ if (info.isRoaming() && !mInfo.isRoamingAllowed()) {
+ throw new StopRequestException(STATUS_WAITING_FOR_NETWORK, "Network is roaming");
}
-
- // if policy has been changed, trigger connectivity check
- if (mPolicyDirty) {
- checkConnectivity();
+ if (info.isMetered() && !mInfo.isMeteredAllowed(mInfoDelta.mTotalBytes)) {
+ throw new StopRequestException(STATUS_WAITING_FOR_NETWORK, "Network is metered");
}
}
@@ -775,17 +830,8 @@ public class DownloadThread implements Runnable {
private void parseUnavailableHeaders(HttpURLConnection conn) {
long retryAfter = conn.getHeaderFieldInt("Retry-After", -1);
- if (retryAfter < 0) {
- retryAfter = 0;
- } else {
- if (retryAfter < Constants.MIN_RETRY_AFTER) {
- retryAfter = Constants.MIN_RETRY_AFTER;
- } else if (retryAfter > Constants.MAX_RETRY_AFTER) {
- retryAfter = Constants.MAX_RETRY_AFTER;
- }
- retryAfter += Helpers.sRandom.nextInt(Constants.MIN_RETRY_AFTER + 1);
- }
-
+ retryAfter = MathUtils.constrain(retryAfter, Constants.MIN_RETRY_AFTER,
+ Constants.MAX_RETRY_AFTER);
mInfoDelta.mRetryAfter = (int) (retryAfter * SECOND_IN_MILLIS);
}
@@ -850,6 +896,22 @@ public class DownloadThread implements Runnable {
// caller is NPMS, since we only register with them
mPolicyDirty = true;
}
+
+ @Override
+ public void onRestrictBackgroundWhitelistChanged(int uid, boolean whitelisted) {
+ // caller is NPMS, since we only register with them
+ if (uid == mInfo.mUid) {
+ mPolicyDirty = true;
+ }
+ }
+
+ @Override
+ public void onRestrictBackgroundBlacklistChanged(int uid, boolean blacklisted) {
+ // caller is NPMS, since we only register with them
+ if (uid == mInfo.mUid) {
+ mPolicyDirty = true;
+ }
+ }
};
private static long getHeaderFieldLong(URLConnection conn, String field, long defaultValue) {
diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java
index d1cc5450..e9549052 100644
--- a/src/com/android/providers/downloads/Helpers.java
+++ b/src/com/android/providers/downloads/Helpers.java
@@ -20,12 +20,22 @@ import static android.os.Environment.buildExternalStorageAppCacheDirs;
import static android.os.Environment.buildExternalStorageAppFilesDirs;
import static android.os.Environment.buildExternalStorageAppMediaDirs;
import static android.os.Environment.buildExternalStorageAppObbDirs;
+import static android.provider.Downloads.Impl.FLAG_REQUIRES_CHARGING;
+import static android.provider.Downloads.Impl.FLAG_REQUIRES_DEVICE_IDLE;
+
import static com.android.providers.downloads.Constants.TAG;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
import android.content.Context;
+import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.storage.StorageManager;
@@ -34,6 +44,8 @@ import android.provider.Downloads;
import android.util.Log;
import android.webkit.MimeTypeMap;
+import com.google.common.annotations.VisibleForTesting;
+
import java.io.File;
import java.io.IOException;
import java.util.Random;
@@ -53,9 +65,116 @@ public class Helpers {
private static final Object sUniqueLock = new Object();
+ private static HandlerThread sAsyncHandlerThread;
+ private static Handler sAsyncHandler;
+
+ private static SystemFacade sSystemFacade;
+ private static DownloadNotifier sNotifier;
+
private Helpers() {
}
+ public synchronized static Handler getAsyncHandler() {
+ if (sAsyncHandlerThread == null) {
+ sAsyncHandlerThread = new HandlerThread("sAsyncHandlerThread",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ sAsyncHandlerThread.start();
+ sAsyncHandler = new Handler(sAsyncHandlerThread.getLooper());
+ }
+ return sAsyncHandler;
+ }
+
+ @VisibleForTesting
+ public synchronized static void setSystemFacade(SystemFacade systemFacade) {
+ sSystemFacade = systemFacade;
+ }
+
+ public synchronized static SystemFacade getSystemFacade(Context context) {
+ if (sSystemFacade == null) {
+ sSystemFacade = new RealSystemFacade(context);
+ }
+ return sSystemFacade;
+ }
+
+ public synchronized static DownloadNotifier getDownloadNotifier(Context context) {
+ if (sNotifier == null) {
+ sNotifier = new DownloadNotifier(context);
+ }
+ return sNotifier;
+ }
+
+ public static String getString(Cursor cursor, String col) {
+ return cursor.getString(cursor.getColumnIndexOrThrow(col));
+ }
+
+ public static int getInt(Cursor cursor, String col) {
+ return cursor.getInt(cursor.getColumnIndexOrThrow(col));
+ }
+
+ public static void scheduleJob(Context context, long downloadId) {
+ final boolean scheduled = scheduleJob(context,
+ DownloadInfo.queryDownloadInfo(context, downloadId));
+ if (!scheduled) {
+ // If we didn't schedule a future job, kick off a notification
+ // update pass immediately
+ getDownloadNotifier(context).update();
+ }
+ }
+
+ /**
+ * Schedule (or reschedule) a job for the given {@link DownloadInfo} using
+ * its current state to define job constraints.
+ */
+ public static boolean scheduleJob(Context context, DownloadInfo info) {
+ if (info == null) return false;
+
+ final JobScheduler scheduler = context.getSystemService(JobScheduler.class);
+
+ // Tear down any existing job for this download
+ final int jobId = (int) info.mId;
+ scheduler.cancel(jobId);
+
+ // Skip scheduling if download is paused or finished
+ if (!info.isReadyToSchedule()) return false;
+
+ final JobInfo.Builder builder = new JobInfo.Builder(jobId,
+ new ComponentName(context, DownloadJobService.class));
+
+ // When this download will show a notification, run with a higher
+ // priority, since it's effectively a foreground service
+ if (info.isVisible()) {
+ builder.setPriority(JobInfo.PRIORITY_FOREGROUND_APP);
+ builder.setFlags(JobInfo.FLAG_WILL_BE_FOREGROUND);
+ }
+
+ // We might have a backoff constraint due to errors
+ final long latency = info.getMinimumLatency();
+ if (latency > 0) {
+ builder.setMinimumLatency(latency);
+ }
+
+ // We always require a network, but the type of network might be further
+ // restricted based on download request or user override
+ builder.setRequiredNetworkType(info.getRequiredNetworkType(info.mTotalBytes));
+
+ if ((info.mFlags & FLAG_REQUIRES_CHARGING) != 0) {
+ builder.setRequiresCharging(true);
+ }
+ if ((info.mFlags & FLAG_REQUIRES_DEVICE_IDLE) != 0) {
+ builder.setRequiresDeviceIdle(true);
+ }
+
+ // If package name was filtered during insert (probably due to being
+ // invalid), blame based on the requesting UID instead
+ String packageName = info.mPackage;
+ if (packageName == null) {
+ packageName = context.getPackageManager().getPackagesForUid(info.mUid)[0];
+ }
+
+ scheduler.scheduleAsPackage(builder.build(), packageName, UserHandle.myUserId(), TAG);
+ return true;
+ }
+
/*
* Parse the Content-Disposition HTTP Header. The format of the header
* is defined here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html
@@ -357,8 +476,6 @@ public class Helpers {
static boolean isFilenameValidInExternalPackage(Context context, File file,
String packageName) {
try {
- file = file.getCanonicalFile();
-
if (containsCanonical(buildExternalStorageAppFilesDirs(packageName), file) ||
containsCanonical(buildExternalStorageAppObbDirs(packageName), file) ||
containsCanonical(buildExternalStorageAppCacheDirs(packageName), file) ||
@@ -380,8 +497,6 @@ public class Helpers {
*/
static boolean isFilenameValid(Context context, File file, boolean allowInternal) {
try {
- file = file.getCanonicalFile();
-
if (allowInternal) {
if (containsCanonical(context.getFilesDir(), file)
|| containsCanonical(context.getCacheDir(), file)
diff --git a/src/com/android/providers/downloads/OpenHelper.java b/src/com/android/providers/downloads/OpenHelper.java
index 4eb319c4..27ab86b9 100644
--- a/src/com/android/providers/downloads/OpenHelper.java
+++ b/src/com/android/providers/downloads/OpenHelper.java
@@ -30,6 +30,8 @@ import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
+import android.os.StrictMode;
+import android.provider.DocumentsContract;
import android.provider.Downloads.Impl.RequestHeaders;
import android.util.Log;
@@ -49,11 +51,14 @@ public class OpenHelper {
intent.addFlags(intentFlags);
try {
+ StrictMode.disableDeathOnFileUriExposure();
context.startActivity(intent);
return true;
} catch (ActivityNotFoundException e) {
Log.w(TAG, "Failed to start " + intent + ": " + e);
return false;
+ } finally {
+ StrictMode.enableDeathOnFileUriExposure();
}
}
@@ -65,6 +70,7 @@ public class OpenHelper {
final DownloadManager downManager = (DownloadManager) context.getSystemService(
Context.DOWNLOAD_SERVICE);
downManager.setAccessAllDownloads(true);
+ downManager.setAccessFilename(true);
final Cursor cursor = downManager.query(new DownloadManager.Query().setFilterById(id));
try {
@@ -77,6 +83,9 @@ public class OpenHelper {
String mimeType = getCursorString(cursor, COLUMN_MEDIA_TYPE);
mimeType = DownloadDrmHelper.getOriginalMimeType(context, file, mimeType);
+ final Uri documentUri = DocumentsContract.buildDocumentUri(
+ Constants.STORAGE_AUTHORITY, String.valueOf(id));
+
final Intent intent = new Intent(Intent.ACTION_VIEW);
if ("application/vnd.android.package-archive".equals(mimeType)) {
@@ -88,14 +97,10 @@ public class OpenHelper {
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())) {
+ } else {
+ intent.setDataAndType(documentUri, mimeType);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- intent.setDataAndType(
- ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id), mimeType);
- } else {
- intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.setDataAndType(localUri, mimeType);
}
return intent;
diff --git a/src/com/android/providers/downloads/RealSystemFacade.java b/src/com/android/providers/downloads/RealSystemFacade.java
index b3f170fb..df1d245f 100644
--- a/src/com/android/providers/downloads/RealSystemFacade.java
+++ b/src/com/android/providers/downloads/RealSystemFacade.java
@@ -16,8 +16,6 @@
package com.android.providers.downloads;
-import com.android.internal.util.ArrayUtils;
-
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
@@ -26,9 +24,17 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ConnectivityManager;
+import android.net.Network;
import android.net.NetworkInfo;
-import android.telephony.TelephonyManager;
-import android.util.Log;
+import android.security.NetworkSecurityPolicy;
+import android.security.net.config.ApplicationConfig;
+
+import java.security.GeneralSecurityException;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+
+import com.android.internal.util.ArrayUtils;
class RealSystemFacade implements SystemFacade {
private Context mContext;
@@ -43,53 +49,27 @@ class RealSystemFacade implements SystemFacade {
}
@Override
- public NetworkInfo getActiveNetworkInfo(int uid) {
- ConnectivityManager connectivity =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- if (connectivity == null) {
- Log.w(Constants.TAG, "couldn't get connectivity manager");
- return null;
- }
-
- final NetworkInfo activeInfo = connectivity.getActiveNetworkInfoForUid(uid);
- if (activeInfo == null && Constants.LOGVV) {
- Log.v(Constants.TAG, "network is not available");
- }
- return activeInfo;
- }
-
- @Override
- public boolean isActiveNetworkMetered() {
- final ConnectivityManager conn = ConnectivityManager.from(mContext);
- return conn.isActiveNetworkMetered();
+ public Network getActiveNetwork(int uid, boolean ignoreBlocked) {
+ return mContext.getSystemService(ConnectivityManager.class)
+ .getActiveNetworkForUid(uid, ignoreBlocked);
}
@Override
- public boolean isNetworkRoaming() {
- ConnectivityManager connectivity =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- if (connectivity == null) {
- Log.w(Constants.TAG, "couldn't get connectivity manager");
- return false;
- }
-
- NetworkInfo info = connectivity.getActiveNetworkInfo();
- boolean isMobile = (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE);
- boolean isRoaming = isMobile && TelephonyManager.getDefault().isNetworkRoaming();
- if (Constants.LOGVV && isRoaming) {
- Log.v(Constants.TAG, "network is roaming");
- }
- return isRoaming;
+ public NetworkInfo getNetworkInfo(Network network, int uid, boolean ignoreBlocked) {
+ return mContext.getSystemService(ConnectivityManager.class)
+ .getNetworkInfoForUid(network, uid, ignoreBlocked);
}
@Override
- public Long getMaxBytesOverMobile() {
- return DownloadManager.getMaxBytesOverMobile(mContext);
+ public long getMaxBytesOverMobile() {
+ final Long value = DownloadManager.getMaxBytesOverMobile(mContext);
+ return (value == null) ? Long.MAX_VALUE : value;
}
@Override
- public Long getRecommendedMaxBytesOverMobile() {
- return DownloadManager.getRecommendedMaxBytesOverMobile(mContext);
+ public long getRecommendedMaxBytesOverMobile() {
+ final Long value = DownloadManager.getRecommendedMaxBytesOverMobile(mContext);
+ return (value == null) ? Long.MAX_VALUE : value;
}
@Override
@@ -121,6 +101,21 @@ class RealSystemFacade implements SystemFacade {
return false;
}
+ @Override
+ public SSLContext getSSLContextForPackage(Context context, String packageName)
+ throws GeneralSecurityException {
+ ApplicationConfig appConfig;
+ try {
+ appConfig = NetworkSecurityPolicy.getApplicationConfigForPackage(context, packageName);
+ } catch (NameNotFoundException e) {
+ // Unknown package -- fallback to the default SSLContext
+ return SSLContext.getDefault();
+ }
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ ctx.init(null, new TrustManager[] {appConfig.getTrustManager()}, null);
+ return ctx;
+ }
+
/**
* Returns whether cleartext network traffic (HTTP) is permitted for the provided package.
*/
diff --git a/src/com/android/providers/downloads/SizeLimitActivity.java b/src/com/android/providers/downloads/SizeLimitActivity.java
deleted file mode 100644
index d25277d9..00000000
--- a/src/com/android/providers/downloads/SizeLimitActivity.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2010 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.app.AlertDialog;
-import android.app.Dialog;
-import android.content.ContentValues;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.Downloads;
-import android.text.format.Formatter;
-import android.util.Log;
-
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * Activity to show dialogs to the user when a download exceeds a limit on download sizes for
- * mobile networks. This activity gets started by the background download service when a download's
- * size is discovered to be exceeded one of these thresholds.
- */
-public class SizeLimitActivity extends Activity
- implements DialogInterface.OnCancelListener, DialogInterface.OnClickListener {
- private Dialog mDialog;
- private Queue<Intent> mDownloadsToShow = new LinkedList<Intent>();
- private Uri mCurrentUri;
- private Intent mCurrentIntent;
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- setIntent(intent);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- Intent intent = getIntent();
- if (intent != null) {
- mDownloadsToShow.add(intent);
- setIntent(null);
- showNextDialog();
- }
- if (mDialog != null && !mDialog.isShowing()) {
- mDialog.show();
- }
- }
-
- private void showNextDialog() {
- if (mDialog != null) {
- return;
- }
-
- if (mDownloadsToShow.isEmpty()) {
- finish();
- return;
- }
-
- mCurrentIntent = mDownloadsToShow.poll();
- mCurrentUri = mCurrentIntent.getData();
- Cursor cursor = getContentResolver().query(mCurrentUri, null, null, null, null);
- try {
- if (!cursor.moveToFirst()) {
- Log.e(Constants.TAG, "Empty cursor for URI " + mCurrentUri);
- dialogClosed();
- return;
- }
- showDialog(cursor);
- } finally {
- cursor.close();
- }
- }
-
- private void showDialog(Cursor cursor) {
- int size = cursor.getInt(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_TOTAL_BYTES));
- String sizeString = Formatter.formatFileSize(this, size);
- String queueText = getString(R.string.button_queue_for_wifi);
- boolean isWifiRequired =
- mCurrentIntent.getExtras().getBoolean(DownloadInfo.EXTRA_IS_WIFI_REQUIRED);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this, AlertDialog.THEME_HOLO_DARK);
- if (isWifiRequired) {
- builder.setTitle(R.string.wifi_required_title)
- .setMessage(getString(R.string.wifi_required_body, sizeString, queueText))
- .setPositiveButton(R.string.button_queue_for_wifi, this)
- .setNegativeButton(R.string.button_cancel_download, this);
- } else {
- builder.setTitle(R.string.wifi_recommended_title)
- .setMessage(getString(R.string.wifi_recommended_body, sizeString, queueText))
- .setPositiveButton(R.string.button_start_now, this)
- .setNegativeButton(R.string.button_queue_for_wifi, this);
- }
- mDialog = builder.setOnCancelListener(this).show();
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- dialogClosed();
- }
-
- private void dialogClosed() {
- mDialog = null;
- mCurrentUri = null;
- showNextDialog();
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- boolean isRequired =
- mCurrentIntent.getExtras().getBoolean(DownloadInfo.EXTRA_IS_WIFI_REQUIRED);
- if (isRequired && which == AlertDialog.BUTTON_NEGATIVE) {
- getContentResolver().delete(mCurrentUri, null, null);
- } else if (!isRequired && which == AlertDialog.BUTTON_POSITIVE) {
- ContentValues values = new ContentValues();
- values.put(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT, true);
- getContentResolver().update(mCurrentUri, values , null, null);
- }
- dialogClosed();
- }
-}
diff --git a/src/com/android/providers/downloads/SystemFacade.java b/src/com/android/providers/downloads/SystemFacade.java
index 83fc7a63..c34317cb 100644
--- a/src/com/android/providers/downloads/SystemFacade.java
+++ b/src/com/android/providers/downloads/SystemFacade.java
@@ -16,41 +16,37 @@
package com.android.providers.downloads;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Network;
import android.net.NetworkInfo;
+import java.security.GeneralSecurityException;
+import javax.net.ssl.SSLContext;
+
interface SystemFacade {
/**
* @see System#currentTimeMillis()
*/
public long currentTimeMillis();
- /**
- * @return Currently active network, or null if there's no active
- * connection.
- */
- public NetworkInfo getActiveNetworkInfo(int uid);
+ public Network getActiveNetwork(int uid, boolean ignoreBlocked);
- public boolean isActiveNetworkMetered();
-
- /**
- * @see android.telephony.TelephonyManager#isNetworkRoaming
- */
- public boolean isNetworkRoaming();
+ public NetworkInfo getNetworkInfo(Network network, int uid, boolean ignoreBlocked);
/**
* @return maximum size, in bytes, of downloads that may go over a mobile connection; or null if
* there's no limit
*/
- public Long getMaxBytesOverMobile();
+ public long getMaxBytesOverMobile();
/**
* @return recommended maximum size, in bytes, of downloads that may go over a mobile
* connection; or null if there's no recommended limit. The user will have the option to bypass
* this limit.
*/
- public Long getRecommendedMaxBytesOverMobile();
+ public long getRecommendedMaxBytesOverMobile();
/**
* Send a broadcast intent.
@@ -66,4 +62,10 @@ interface SystemFacade {
* Returns true if cleartext network traffic is permitted for the specified UID.
*/
public boolean isCleartextTrafficPermitted(int uid);
+
+ /**
+ * Return a {@link SSLContext} configured using the specified package's configuration.
+ */
+ public SSLContext getSSLContextForPackage(Context context, String pckg)
+ throws GeneralSecurityException;
}
diff --git a/tests/public_api_access/src/com/android/providers/downloads/public_api_access_tests/PublicApiAccessTest.java b/tests/public_api_access/src/com/android/providers/downloads/public_api_access_tests/PublicApiAccessTest.java
index 76339415..82fa9346 100644
--- a/tests/public_api_access/src/com/android/providers/downloads/public_api_access_tests/PublicApiAccessTest.java
+++ b/tests/public_api_access/src/com/android/providers/downloads/public_api_access_tests/PublicApiAccessTest.java
@@ -51,7 +51,7 @@ public class PublicApiAccessTest extends AndroidTestCase {
protected void setUp() throws Exception {
super.setUp();
mContentResolver = getContext().getContentResolver();
- mManager = new DownloadManager(mContentResolver, getContext().getPackageName());
+ mManager = new DownloadManager(getContext());
}
@Override
diff --git a/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
index 28c5dc7d..0330fd38 100644
--- a/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
+++ b/tests/src/com/android/providers/downloads/AbstractDownloadProviderFunctionalTest.java
@@ -17,12 +17,15 @@
package com.android.providers.downloads;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import android.app.DownloadManager;
import android.app.NotificationManager;
-import android.content.ComponentName;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
+import android.content.pm.ProviderInfo;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
@@ -47,7 +50,7 @@ import java.net.MalformedURLException;
import java.net.UnknownHostException;
public abstract class AbstractDownloadProviderFunctionalTest extends
- ServiceTestCase<DownloadService> {
+ ServiceTestCase<DownloadJobService> {
protected static final String LOG_TAG = "DownloadProviderFunctionalTest";
private static final String PROVIDER_AUTHORITY = "downloads";
@@ -99,13 +102,15 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
private final ContentResolver mResolver;
private final NotificationManager mNotifManager;
-
- boolean mHasServiceBeenStarted = false;
+ private final DownloadManager mDownloadManager;
+ private final JobScheduler mJobScheduler;
public TestContext(Context realContext) {
super(realContext, FILENAME_PREFIX);
mResolver = new MockContentResolverWithNotify(this);
mNotifManager = mock(NotificationManager.class);
+ mDownloadManager = mock(DownloadManager.class);
+ mJobScheduler = mock(JobScheduler.class);
}
/**
@@ -123,26 +128,18 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
public Object getSystemService(String name) {
if (Context.NOTIFICATION_SERVICE.equals(name)) {
return mNotifManager;
+ } else if (Context.DOWNLOAD_SERVICE.equals(name)) {
+ return mDownloadManager;
+ } else if (Context.JOB_SCHEDULER_SERVICE.equals(name)) {
+ return mJobScheduler;
}
return super.getSystemService(name);
}
-
- /**
- * Record when DownloadProvider starts DownloadService.
- */
- @Override
- public ComponentName startService(Intent service) {
- if (service.getComponent().getClassName().equals(DownloadService.class.getName())) {
- mHasServiceBeenStarted = true;
- return service.getComponent();
- }
- throw new UnsupportedOperationException("Unexpected service: " + service);
- }
}
public AbstractDownloadProviderFunctionalTest(FakeSystemFacade systemFacade) {
- super(DownloadService.class);
+ super(DownloadJobService.class);
mSystemFacade = systemFacade;
}
@@ -162,13 +159,16 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
final DownloadProvider provider = new DownloadProvider();
provider.mSystemFacade = mSystemFacade;
- provider.attachInfo(mTestContext, null);
+
+ ProviderInfo info = new ProviderInfo();
+ info.authority = "downloads";
+ provider.attachInfo(mTestContext, info);
mResolver.addProvider(PROVIDER_AUTHORITY, provider);
setContext(mTestContext);
setupService();
- getService().mSystemFacade = mSystemFacade;
+ Helpers.setSystemFacade(mSystemFacade);
mSystemFacade.setUp();
assertTrue(isDatabaseEmpty()); // ensure we're not messing with real data
@@ -184,6 +184,12 @@ public abstract class AbstractDownloadProviderFunctionalTest extends
super.tearDown();
}
+ protected void startDownload(long id) {
+ final JobParameters params = mock(JobParameters.class);
+ when(params.getJobId()).thenReturn((int) id);
+ getService().onStartJob(params);
+ }
+
private boolean isDatabaseEmpty() {
Cursor cursor = mResolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
null, null, null, null);
diff --git a/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java b/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java
index 2846c7af..3a585b47 100644
--- a/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java
+++ b/tests/src/com/android/providers/downloads/AbstractPublicApiTest.java
@@ -22,6 +22,8 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import android.app.DownloadManager;
+import android.content.ContentResolver;
+import android.content.ContextWrapper;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
@@ -113,13 +115,13 @@ public abstract class AbstractPublicApiTest extends AbstractDownloadProviderFunc
void runUntilStatus(int status) throws TimeoutException {
final long startMillis = mSystemFacade.currentTimeMillis();
- startService(null);
+ startDownload(mId);
waitForStatus(status, startMillis);
}
void runUntilStatus(int status, long timeout) throws TimeoutException {
final long startMillis = mSystemFacade.currentTimeMillis();
- startService(null);
+ startDownload(mId);
waitForStatus(status, startMillis, timeout);
}
@@ -167,7 +169,7 @@ public abstract class AbstractPublicApiTest extends AbstractDownloadProviderFunc
// waits until progress_so_far is >= (progress)%
boolean runUntilProgress(int progress) throws InterruptedException {
- startService(null);
+ startDownload(mId);
int sleepCounter = MAX_TIME_TO_WAIT_FOR_OPERATION * 1000 / TIME_TO_SLEEP;
int numBytesReceivedSoFar = 0;
@@ -217,7 +219,18 @@ public abstract class AbstractPublicApiTest extends AbstractDownloadProviderFunc
@Override
protected void setUp() throws Exception {
super.setUp();
- mManager = new DownloadManager(mResolver, PACKAGE_NAME);
+ mManager = new DownloadManager(new ContextWrapper(mContext) {
+ @Override
+ public ContentResolver getContentResolver() {
+ return mResolver;
+ }
+
+ @Override
+ public String getPackageName() {
+ return PACKAGE_NAME;
+ }
+ });
+ mManager.setAccessFilename(true);
}
protected DownloadManager.Request getRequest()
diff --git a/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java b/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java
index 41dff672..9a4e6444 100644
--- a/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java
+++ b/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java
@@ -17,8 +17,10 @@
package com.android.providers.downloads;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+
import static java.net.HttpURLConnection.HTTP_OK;
+import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.ConnectivityManager;
@@ -56,7 +58,6 @@ public class DownloadProviderFunctionalTest extends AbstractDownloadProviderFunc
String path = "/download_manager_test_path";
Uri downloadUri = requestDownload(path);
assertEquals(Downloads.Impl.STATUS_PENDING, getDownloadStatus(downloadUri));
- assertTrue(mTestContext.mHasServiceBeenStarted);
runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS);
RecordedRequest request = takeRequest();
@@ -108,13 +109,11 @@ public class DownloadProviderFunctionalTest extends AbstractDownloadProviderFunc
// Assert that HTTP request succeeds when cleartext traffic is permitted
mSystemFacade.mCleartextTrafficPermitted = true;
Uri downloadUri = requestDownload("/path");
- assertEquals("http", downloadUri.getScheme());
runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS);
// Assert that HTTP request fails when cleartext traffic is not permitted
mSystemFacade.mCleartextTrafficPermitted = false;
downloadUri = requestDownload("/path");
- assertEquals("http", downloadUri.getScheme());
runUntilStatus(downloadUri, Downloads.Impl.STATUS_BAD_REQUEST);
}
@@ -131,8 +130,8 @@ public class DownloadProviderFunctionalTest extends AbstractDownloadProviderFunc
}
private void runUntilStatus(Uri downloadUri, int expected) throws Exception {
- startService(null);
-
+ startDownload(ContentUris.parseId(downloadUri));
+
int actual = -1;
final long timeout = SystemClock.elapsedRealtime() + (15 * SECOND_IN_MILLIS);
@@ -149,7 +148,7 @@ public class DownloadProviderFunctionalTest extends AbstractDownloadProviderFunc
}
protected int getDownloadStatus(Uri downloadUri) {
- return Integer.valueOf(getDownloadField(downloadUri, Downloads.Impl.COLUMN_STATUS));
+ return Integer.parseInt(getDownloadField(downloadUri, Downloads.Impl.COLUMN_STATUS));
}
private String getDownloadFilename(Uri downloadUri) {
diff --git a/tests/src/com/android/providers/downloads/FakeSystemFacade.java b/tests/src/com/android/providers/downloads/FakeSystemFacade.java
index 7581e6fa..b6b800a2 100644
--- a/tests/src/com/android/providers/downloads/FakeSystemFacade.java
+++ b/tests/src/com/android/providers/downloads/FakeSystemFacade.java
@@ -1,33 +1,54 @@
package com.android.providers.downloads;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ConnectivityManager;
+import android.net.Network;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
+import javax.net.ssl.SSLContext;
+
public class FakeSystemFacade implements SystemFacade {
long mTimeMillis = 0;
Integer mActiveNetworkType = ConnectivityManager.TYPE_WIFI;
boolean mIsRoaming = false;
boolean mIsMetered = false;
- Long mMaxBytesOverMobile = null;
- Long mRecommendedMaxBytesOverMobile = null;
+ long mMaxBytesOverMobile = Long.MAX_VALUE;
+ long mRecommendedMaxBytesOverMobile = Long.MAX_VALUE;
List<Intent> mBroadcastsSent = new ArrayList<Intent>();
boolean mCleartextTrafficPermitted = true;
private boolean mReturnActualTime = false;
+ private SSLContext mSSLContext = null;
public void setUp() {
mTimeMillis = 0;
mActiveNetworkType = ConnectivityManager.TYPE_WIFI;
mIsRoaming = false;
mIsMetered = false;
- mMaxBytesOverMobile = null;
- mRecommendedMaxBytesOverMobile = null;
+ mMaxBytesOverMobile = Long.MAX_VALUE;
+ mRecommendedMaxBytesOverMobile = Long.MAX_VALUE;
mBroadcastsSent.clear();
mReturnActualTime = false;
+ try {
+ mSSLContext = SSLContext.getDefault();
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
}
void incrementTimeMillis(long delta) {
@@ -43,33 +64,45 @@ public class FakeSystemFacade implements SystemFacade {
}
@Override
- public NetworkInfo getActiveNetworkInfo(int uid) {
+ public Network getActiveNetwork(int uid, boolean ignoreBlocked) {
if (mActiveNetworkType == null) {
return null;
} else {
- final NetworkInfo info = new NetworkInfo(mActiveNetworkType, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
- return info;
+ final Network network = mock(Network.class);
+ try {
+ when(network.openConnection(any())).then(new Answer<URLConnection>() {
+ @Override
+ public URLConnection answer(InvocationOnMock invocation) throws Throwable {
+ final URL url = (URL) invocation.getArguments()[0];
+ return url.openConnection();
+ }
+ });
+ } catch (IOException ignored) {
+ }
+ return network;
}
}
@Override
- public boolean isActiveNetworkMetered() {
- return mIsMetered;
- }
-
- @Override
- public boolean isNetworkRoaming() {
- return mIsRoaming;
+ public NetworkInfo getNetworkInfo(Network network, int uid, boolean ignoreBlocked) {
+ if (mActiveNetworkType == null) {
+ return null;
+ } else {
+ final NetworkInfo info = new NetworkInfo(mActiveNetworkType, 0, null, null);
+ info.setDetailedState(DetailedState.CONNECTED, null, null);
+ info.setRoaming(mIsRoaming);
+ info.setMetered(mIsMetered);
+ return info;
+ }
}
@Override
- public Long getMaxBytesOverMobile() {
+ public long getMaxBytesOverMobile() {
return mMaxBytesOverMobile;
}
@Override
- public Long getRecommendedMaxBytesOverMobile() {
+ public long getRecommendedMaxBytesOverMobile() {
return mRecommendedMaxBytesOverMobile;
}
@@ -88,6 +121,15 @@ public class FakeSystemFacade implements SystemFacade {
return mCleartextTrafficPermitted;
}
+ @Override
+ public SSLContext getSSLContextForPackage(Context context, String pckg) {
+ return mSSLContext;
+ }
+
+ public void setSSLContext(SSLContext context) {
+ mSSLContext = context;
+ }
+
public void setReturnActualTime(boolean flag) {
mReturnActualTime = flag;
}
diff --git a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
index d1048b02..97bc4a22 100644
--- a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
+++ b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
@@ -20,12 +20,7 @@ import static android.app.DownloadManager.STATUS_FAILED;
import static android.app.DownloadManager.STATUS_PAUSED;
import static android.net.TrafficStats.GB_IN_BYTES;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
-import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
-import static java.net.HttpURLConnection.HTTP_OK;
-import static java.net.HttpURLConnection.HTTP_PARTIAL;
-import static java.net.HttpURLConnection.HTTP_PRECON_FAILED;
-import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
+
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.isA;
@@ -34,10 +29,16 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static java.net.HttpURLConnection.HTTP_PARTIAL;
+import static java.net.HttpURLConnection.HTTP_PRECON_FAILED;
+import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
+
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;
@@ -49,12 +50,12 @@ import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.Suppress;
import android.text.format.DateUtils;
+import libcore.io.IoUtils;
+
import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.RecordedRequest;
import com.google.mockwebserver.SocketPolicy;
-import libcore.io.IoUtils;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -71,6 +72,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
protected File mTestDirectory;
private NotificationManager mNotifManager;
+ private DownloadManager mDownloadManager;
public PublicApiFunctionalTest() {
super(new FakeSystemFacade());
@@ -80,8 +82,8 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
protected void setUp() throws Exception {
super.setUp();
- mNotifManager = (NotificationManager) getContext()
- .getSystemService(Context.NOTIFICATION_SERVICE);
+ mNotifManager = getContext().getSystemService(NotificationManager.class);
+ mDownloadManager = getContext().getSystemService(DownloadManager.class);
mTestDirectory = new File(Environment.getExternalStorageDirectory() + File.separator
+ "download_manager_functional_test");
@@ -393,10 +395,12 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
mSystemFacade.mMaxBytesOverMobile = (long) FILE_CONTENT.length() - 1;
mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE;
+ mSystemFacade.mIsMetered = true;
Download download = enqueueRequest(getRequest());
download.runUntilStatus(DownloadManager.STATUS_PAUSED);
mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI;
+ mSystemFacade.mIsMetered = false;
// first response was read, but aborted after the DL manager processed the Content-Length
// header, so we need to enqueue a second one
download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
@@ -539,7 +543,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
Download download = enqueueRequest(getRequest());
DownloadReceiver receiver = new DownloadReceiver();
- receiver.mSystemFacade = mSystemFacade;
+ Helpers.setSystemFacade(mSystemFacade);
Intent intent = new Intent(Constants.ACTION_LIST);
intent.setData(Uri.parse(Downloads.Impl.CONTENT_URI + "/" + download.mId));
intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS,
@@ -552,6 +556,23 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
assertEquals(PACKAGE_NAME, broadcast.getPackage());
}
+ public void testNotificationCancelDownloadClicked() throws Exception {
+ Download download = enqueueRequest(getRequest());
+
+ DownloadReceiver receiver = new DownloadReceiver();
+ Helpers.setSystemFacade(mSystemFacade);
+ Intent intent = new Intent(Constants.ACTION_CANCEL);
+ intent.setData(Uri.parse(Downloads.Impl.CONTENT_URI + "/" + download.mId));
+
+ long[] downloadIds = {download.mId};
+ intent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_IDS, downloadIds);
+ intent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_NOTIFICATION_TAG, "tag");
+ receiver.onReceive(mContext, intent);
+
+ verify(mNotifManager, times(1)).cancel("tag", 0);
+ verify(mDownloadManager, times(1)).remove(downloadIds);
+ }
+
public void testBasicConnectivityChanges() throws Exception {
enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT));
@@ -570,6 +591,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
enqueueResponse(buildEmptyResponse(HTTP_OK));
mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_MOBILE;
+ mSystemFacade.mIsMetered = true;
// by default, use any connection
Download download = enqueueRequest(getRequest());
@@ -581,6 +603,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
download.runUntilStatus(DownloadManager.STATUS_PAUSED);
// ...then enable wifi
mSystemFacade.mActiveNetworkType = ConnectivityManager.TYPE_WIFI;
+ mSystemFacade.mIsMetered = false;
download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
}
@@ -610,6 +633,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
assertTrue(mResolver.mNotifyWasCalled);
}
+ @Suppress
public void testNotificationNever() throws Exception {
enqueueResponse(buildEmptyResponse(HTTP_OK));
@@ -617,10 +641,11 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
getRequest().setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN));
download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
- verify(mNotifManager, times(1)).cancelAll();
+ // TODO: verify different notif types with tags
verify(mNotifManager, never()).notify(anyString(), anyInt(), isA(Notification.class));
}
+ @Suppress
public void testNotificationVisible() throws Exception {
enqueueResponse(buildEmptyResponse(HTTP_OK));
@@ -629,10 +654,10 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
// TODO: verify different notif types with tags
- verify(mNotifManager, times(1)).cancelAll();
verify(mNotifManager, atLeastOnce()).notify(anyString(), anyInt(), isA(Notification.class));
}
+ @Suppress
public void testNotificationVisibleComplete() throws Exception {
enqueueResponse(buildEmptyResponse(HTTP_OK));
@@ -641,7 +666,6 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
// TODO: verify different notif types with tags
- verify(mNotifManager, times(1)).cancelAll();
verify(mNotifManager, atLeastOnce()).notify(anyString(), anyInt(), isA(Notification.class));
}
diff --git a/tests/src/com/android/providers/downloads/StorageTest.java b/tests/src/com/android/providers/downloads/StorageTest.java
index 8ba3cbce..95bd3676 100644
--- a/tests/src/com/android/providers/downloads/StorageTest.java
+++ b/tests/src/com/android/providers/downloads/StorageTest.java
@@ -32,6 +32,7 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStatVfs;
import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import com.android.providers.downloads.StorageUtils.ObserverLatch;
@@ -47,6 +48,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
+@MediumTest
public class StorageTest extends AbstractPublicApiTest {
private static final String TAG = "StorageTest";
diff --git a/tests/src/com/android/providers/downloads/ThreadingTest.java b/tests/src/com/android/providers/downloads/ThreadingTest.java
index 1e501444..dda4db5e 100644
--- a/tests/src/com/android/providers/downloads/ThreadingTest.java
+++ b/tests/src/com/android/providers/downloads/ThreadingTest.java
@@ -46,19 +46,6 @@ public class ThreadingTest extends AbstractPublicApiTest {
super.tearDown();
}
- /**
- * Test for race conditions when the service is flooded with startService() calls while running
- * a download.
- */
- public void testFloodServiceWithStarts() throws Exception {
- enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT));
- Download download = enqueueRequest(getRequest());
- while (download.getStatus() != DownloadManager.STATUS_SUCCESSFUL) {
- startService(null);
- Thread.sleep(10);
- }
- }
-
public void testFilenameRace() throws Exception {
final List<Pair<Download, String>> downloads = Lists.newArrayList();
final HashSet<String> expectedBodies = Sets.newHashSet();
@@ -73,12 +60,10 @@ public class ThreadingTest extends AbstractPublicApiTest {
final Download d = enqueueRequest(getRequest());
downloads.add(Pair.create(d, body));
expectedBodies.add(body);
+ startDownload(d.mId);
}
- // Kick off downloads in parallel
final long startMillis = mSystemFacade.currentTimeMillis();
- startService(null);
-
for (Pair<Download,String> d : downloads) {
d.first.waitForStatus(DownloadManager.STATUS_SUCCESSFUL, startMillis);
}
diff --git a/ui/AndroidManifest.xml b/ui/AndroidManifest.xml
index e93e268c..fa795c9f 100644
--- a/ui/AndroidManifest.xml
+++ b/ui/AndroidManifest.xml
@@ -14,19 +14,6 @@
android:requiredForAllUsers="true">
<activity
- android:name=".DownloadList"
- android:theme="@android:style/Theme.NoDisplay">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.VIEW_DOWNLOADS" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
-
- <activity
android:name=".TrampolineActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:permission="android.permission.MANAGE_DOCUMENTS">
diff --git a/ui/res/values-af/strings.xml b/ui/res/values-af/strings.xml
index 6da14e0e..996f81ec 100644
--- a/ui/res/values-af/strings.xml
+++ b/ui/res/values-af/strings.xml
@@ -16,10 +16,10 @@
<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">"Aflaaisels"</string>
- <string name="download_title_sorted_by_date" msgid="5898014492155434221">"Aflaaisels - gerangskik volgens datum"</string>
- <string name="download_title_sorted_by_size" msgid="1417193166677094813">"Aflaaisels - gerangskik volgens grootte"</string>
- <string name="no_downloads" msgid="1029667411186146836">"Geen aflaaisels nie."</string>
+ <string name="app_label" msgid="3070921713463294774">"Aflaaie"</string>
+ <string name="download_title_sorted_by_date" msgid="5898014492155434221">"Aflaaie - gerangskik volgens datum"</string>
+ <string name="download_title_sorted_by_size" msgid="1417193166677094813">"Aflaaie - gerangskik volgens grootte"</string>
+ <string name="no_downloads" msgid="1029667411186146836">"Geen aflaaie nie."</string>
<string name="missing_title" msgid="830115697868833773">"&lt;Onbekend&gt;"</string>
<string name="button_sort_by_size" msgid="7331549713691146251">"Rangskik volgens grootte"</string>
<string name="button_sort_by_date" msgid="8800842892684101528">"Rangskik volgens datum"</string>
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Behou"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Kanselleer"</string>
<string name="retry_download" msgid="7617100787922717912">"Probeer weer"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Begin nou"</string>
<string name="deselect_all" msgid="6348198946254776764">"Ontkies almal"</string>
<string name="select_all" msgid="634074918366265804">"Kies almal"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="NUMBER">%1$d</xliff:g> gekies uit <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-am/strings.xml b/ui/res/values-am/strings.xml
index 42626740..dc9cec41 100644
--- a/ui/res/values-am/strings.xml
+++ b/ui/res/values-am/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"ጠብቅ"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"ይቅር"</string>
<string name="retry_download" msgid="7617100787922717912">"እንደገና ሞክር"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"አሁን ጀምር"</string>
<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>
diff --git a/ui/res/values-ar/strings.xml b/ui/res/values-ar/strings.xml
index 99638b35..1544e54d 100644
--- a/ui/res/values-ar/strings.xml
+++ b/ui/res/values-ar/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"مواصلة"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"إلغاء"</string>
<string name="retry_download" msgid="7617100787922717912">"إعادة المحاولة"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"البدء الآن"</string>
<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>
diff --git a/ui/res/values-az-rAZ/strings.xml b/ui/res/values-az-rAZ/strings.xml
index 5ee50dfe..1741b87f 100644
--- a/ui/res/values-az-rAZ/strings.xml
+++ b/ui/res/values-az-rAZ/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Saxla"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Ləğv et"</string>
<string name="retry_download" msgid="7617100787922717912">"Yeniden yoxla"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"İndi başlayın"</string>
<string name="deselect_all" msgid="6348198946254776764">"Hamısını qaldır"</string>
<string name="select_all" msgid="634074918366265804">"Hamısını seç"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g> içindən seçilmiş <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
diff --git a/ui/res/values-b+sr+Latn/strings.xml b/ui/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 00000000..b4cd8bc2
--- /dev/null
+++ b/ui/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2010 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.
+ -->
+
+<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">"Preuzimanja"</string>
+ <string name="download_title_sorted_by_date" msgid="5898014492155434221">"Preuzimanja – Sortirano prema datumu"</string>
+ <string name="download_title_sorted_by_size" msgid="1417193166677094813">"Preuzimanja – Sortirano prema veličini"</string>
+ <string name="no_downloads" msgid="1029667411186146836">"Nema preuzimanja."</string>
+ <string name="missing_title" msgid="830115697868833773">"&lt;Nepoznato&gt;"</string>
+ <string name="button_sort_by_size" msgid="7331549713691146251">"Sortiraj prema veličini"</string>
+ <string name="button_sort_by_date" msgid="8800842892684101528">"Sortiraj prema datumu"</string>
+ <string name="download_queued" msgid="104973307780629904">"Stavljeno na čekanje"</string>
+ <string name="download_running" msgid="4656462962155580641">"U toku"</string>
+ <string name="download_success" msgid="7006048006543495236">"Dovršeno"</string>
+ <string name="download_error" msgid="8081329546008568251">"Neuspešno"</string>
+ <string name="dialog_title_not_available" msgid="5746317632356158515">"Preuzimanje nije moguće"</string>
+ <string name="dialog_failed_body" msgid="587545111677064427">"Želite li da kasnije probate da preuzmete datoteku ili želite da je izbrišete iz reda?"</string>
+ <string name="dialog_title_queued_body" msgid="6760681913815015219">"Stavljena je u red"</string>
+ <string name="dialog_queued_body" msgid="708552801635572720">"Ova datoteka je stavljena u red za preuzimanje, pa još uvek nije dostupna."</string>
+ <string name="dialog_file_missing_body" msgid="3223012612774276284">"Nije moguće pronaći preuzetu datoteku."</string>
+ <string name="dialog_insufficient_space_on_external" msgid="8692452156251449195">"Nije moguće završiti preuzimanje. Nema dovoljno prostora u eksternoj memoriji."</string>
+ <string name="dialog_insufficient_space_on_cache" msgid="6313630206163908994">"Nije moguće završiti preuzimanje. Nema dovoljno prostora u internoj memoriji za preuzimanje."</string>
+ <string name="dialog_cannot_resume" msgid="8664509751358983543">"Preuzimanje je prekinuto i nije ga moguće nastaviti."</string>
+ <string name="dialog_file_already_exists" msgid="8308563940663449590">"Preuzimanje nije moguće. Odredišna datoteka već postoji."</string>
+ <string name="dialog_media_not_found" msgid="4468088418758018765">"Preuzimanje nije moguće. Eksterni medijum nije dostupan."</string>
+ <string name="download_no_application_title" msgid="7024782176657362251">"Nije moguće otvoriti datoteku"</string>
+ <string name="remove_download" msgid="6372920256257247857">"Ukloni"</string>
+ <string name="delete_download" msgid="76629022653866471">"Izbriši"</string>
+ <string name="keep_queued_download" msgid="5144882786014818569">"Zadrži"</string>
+ <string name="cancel_running_download" msgid="5232704030969221112">"Otkaži"</string>
+ <string name="retry_download" msgid="7617100787922717912">"Pokušaj ponovo"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Započni odmah"</string>
+ <string name="deselect_all" msgid="6348198946254776764">"Opozovi sve izbore"</string>
+ <string name="select_all" msgid="634074918366265804">"Izaberi sve"</string>
+ <string name="selected_count" msgid="2101564570019753277">"Izabrano je <xliff:g id="NUMBER">%1$d</xliff:g> od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="download_share_dialog" msgid="3355867339806448955">"Deljenje preko"</string>
+</resources>
diff --git a/ui/res/values-be-rBY/strings.xml b/ui/res/values-be-rBY/strings.xml
new file mode 100644
index 00000000..a035f113
--- /dev/null
+++ b/ui/res/values-be-rBY/strings.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2010 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.
+ -->
+
+<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="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>
+ <string name="missing_title" msgid="830115697868833773">"Невядомы"</string>
+ <string name="button_sort_by_size" msgid="7331549713691146251">"Сартаваць паводле памеру"</string>
+ <string name="button_sort_by_date" msgid="8800842892684101528">"Сартаваць паводле даты"</string>
+ <string name="download_queued" msgid="104973307780629904">"У чарзе"</string>
+ <string name="download_running" msgid="4656462962155580641">"Выконваецца"</string>
+ <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_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">"Немагчыма завершыць спампоўку. Не хапае месца на знешнім назапашвальнiку."</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">"Немагчыма спампаваць. Не падключаны знешнi назапашвальнiк."</string>
+ <string name="download_no_application_title" msgid="7024782176657362251">"Немагчыма адкрыць файл"</string>
+ <string name="remove_download" msgid="6372920256257247857">"Выдаліць"</string>
+ <string name="delete_download" msgid="76629022653866471">"Выдаліць"</string>
+ <string name="keep_queued_download" msgid="5144882786014818569">"Пакінуць"</string>
+ <string name="cancel_running_download" msgid="5232704030969221112">"Скасаваць"</string>
+ <string name="retry_download" msgid="7617100787922717912">"Паўтарыць"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Пачаць"</string>
+ <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>
+</resources>
diff --git a/ui/res/values-bg/strings.xml b/ui/res/values-bg/strings.xml
index 98ea0292..90b2e66f 100644
--- a/ui/res/values-bg/strings.xml
+++ b/ui/res/values-bg/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Съхраняване"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Отказ"</string>
<string name="retry_download" msgid="7617100787922717912">"Повторен опит"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Стартиране сега"</string>
<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>
diff --git a/ui/res/values-bn-rBD/strings.xml b/ui/res/values-bn-rBD/strings.xml
index 5aca3712..23f906e1 100644
--- a/ui/res/values-bn-rBD/strings.xml
+++ b/ui/res/values-bn-rBD/strings.xml
@@ -43,8 +43,9 @@
<string name="keep_queued_download" msgid="5144882786014818569">"রাখুন"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"বাতিল করুন"</string>
<string name="retry_download" msgid="7617100787922717912">"পুনরায় চেষ্টা করুন"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"এখন শুরু করুন"</string>
<string name="deselect_all" msgid="6348198946254776764">"সবগুলিকে নির্বাচনমুক্ত করুন"</string>
<string name="select_all" msgid="634074918366265804">"সবগুলি নির্বাচন করুন"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g>টির মধ্যে <xliff:g id="NUMBER">%1$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-bs-rBA/strings.xml b/ui/res/values-bs-rBA/strings.xml
new file mode 100644
index 00000000..ad41199b
--- /dev/null
+++ b/ui/res/values-bs-rBA/strings.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2010 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.
+ -->
+
+<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">"Preuzimanja"</string>
+ <string name="download_title_sorted_by_date" msgid="5898014492155434221">"Preuzimanja - sortirana po datumu"</string>
+ <string name="download_title_sorted_by_size" msgid="1417193166677094813">"Preuzimanja - sortirana po veličini"</string>
+ <string name="no_downloads" msgid="1029667411186146836">"Nema preuzimanja."</string>
+ <string name="missing_title" msgid="830115697868833773">"&lt;Nepoznato&gt;"</string>
+ <string name="button_sort_by_size" msgid="7331549713691146251">"Sortiraj po veličini"</string>
+ <string name="button_sort_by_date" msgid="8800842892684101528">"Sortiraj po datumu"</string>
+ <string name="download_queued" msgid="104973307780629904">"U redoslijedu"</string>
+ <string name="download_running" msgid="4656462962155580641">"U toku"</string>
+ <string name="download_success" msgid="7006048006543495236">"Završeno"</string>
+ <string name="download_error" msgid="8081329546008568251">"Neuspješno"</string>
+ <string name="dialog_title_not_available" msgid="5746317632356158515">"Nije bilo moguće preuzeti"</string>
+ <string name="dialog_failed_body" msgid="587545111677064427">"Da li želite ponovo pokušati s preuzimanjem datoteke kasnije ili je želite izbrisati s redoslijeda?"</string>
+ <string name="dialog_title_queued_body" msgid="6760681913815015219">"Datoteka je u redoslijedu"</string>
+ <string name="dialog_queued_body" msgid="708552801635572720">"Datoteka je na redoslijedu za kasnije preuzimanje tako da još nije dostupna."</string>
+ <string name="dialog_file_missing_body" msgid="3223012612774276284">"Nije moguće pronaći preuzetu datoteku."</string>
+ <string name="dialog_insufficient_space_on_external" msgid="8692452156251449195">"Nije moguće dovršiti preuzimanje. Nema dovoljno prostora na eksternoj pohrani."</string>
+ <string name="dialog_insufficient_space_on_cache" msgid="6313630206163908994">"Nije moguće dovršiti preuzimanje. Nema dovoljno prostora na internoj pohrani za preuzimanja."</string>
+ <string name="dialog_cannot_resume" msgid="8664509751358983543">"Preuzimanje je prekinuto i ne može se nastaviti."</string>
+ <string name="dialog_file_already_exists" msgid="8308563940663449590">"Nije moguće preuzeti. Odredišna datoteka već postoji."</string>
+ <string name="dialog_media_not_found" msgid="4468088418758018765">"Nije moguće preuzeti. Eksterni medij nije dostupan."</string>
+ <string name="download_no_application_title" msgid="7024782176657362251">"Nije moguće otvoriti datoteku"</string>
+ <string name="remove_download" msgid="6372920256257247857">"Ukloni"</string>
+ <string name="delete_download" msgid="76629022653866471">"Izbriši"</string>
+ <string name="keep_queued_download" msgid="5144882786014818569">"Zadrži"</string>
+ <string name="cancel_running_download" msgid="5232704030969221112">"Otkaži"</string>
+ <string name="retry_download" msgid="7617100787922717912">"Pokušaj ponovo"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Pokreni odmah"</string>
+ <string name="deselect_all" msgid="6348198946254776764">"Poništi odabir svih"</string>
+ <string name="select_all" msgid="634074918366265804">"Odaberi sve"</string>
+ <string name="selected_count" msgid="2101564570019753277">"Odabrano <xliff:g id="NUMBER">%1$d</xliff:g> od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="download_share_dialog" msgid="3355867339806448955">"Podijeli preko"</string>
+</resources>
diff --git a/ui/res/values-ca/strings.xml b/ui/res/values-ca/strings.xml
index f9fc286c..3892888a 100644
--- a/ui/res/values-ca/strings.xml
+++ b/ui/res/values-ca/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Conserva"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Cancel·la"</string>
<string name="retry_download" msgid="7617100787922717912">"Torna-ho a provar"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Comença ara"</string>
<string name="deselect_all" msgid="6348198946254776764">"Anul·la la selecció de tot"</string>
<string name="select_all" msgid="634074918366265804">"Selecciona-ho tot"</string>
<string name="selected_count" msgid="2101564570019753277">"S\'han seleccionat <xliff:g id="NUMBER">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-cs/strings.xml b/ui/res/values-cs/strings.xml
index 40d7c426..66c51ac9 100644
--- a/ui/res/values-cs/strings.xml
+++ b/ui/res/values-cs/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Zachovat"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Zrušit"</string>
<string name="retry_download" msgid="7617100787922717912">"Zkusit znovu"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Spustit"</string>
<string name="deselect_all" msgid="6348198946254776764">"Zrušit celý výběr"</string>
<string name="select_all" msgid="634074918366265804">"Vybrat vše"</string>
<string name="selected_count" msgid="2101564570019753277">"Vybráno: <xliff:g id="NUMBER">%1$d</xliff:g> z <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-da/strings.xml b/ui/res/values-da/strings.xml
index 595bd8c1..3c79f23b 100644
--- a/ui/res/values-da/strings.xml
+++ b/ui/res/values-da/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Behold"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Annuller"</string>
<string name="retry_download" msgid="7617100787922717912">"Prøv igen"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Start nu"</string>
<string name="deselect_all" msgid="6348198946254776764">"Fjern markering af alle"</string>
<string name="select_all" msgid="634074918366265804">"Markér alle"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="NUMBER">%1$d</xliff:g> valgte ud af <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-de/strings.xml b/ui/res/values-de/strings.xml
index 936430ab..bfd57060 100644
--- a/ui/res/values-de/strings.xml
+++ b/ui/res/values-de/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Beibehalten"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Abbrechen"</string>
<string name="retry_download" msgid="7617100787922717912">"Wiederholen"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Jetzt starten"</string>
<string name="deselect_all" msgid="6348198946254776764">"Auswahl für alle aufheben"</string>
<string name="select_all" msgid="634074918366265804">"Alle auswählen"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="NUMBER">%1$d</xliff:g> von <xliff:g id="TOTAL">%2$d</xliff:g> ausgewählt"</string>
diff --git a/ui/res/values-el/strings.xml b/ui/res/values-el/strings.xml
index 369d05a3..bf1c04ca 100644
--- a/ui/res/values-el/strings.xml
+++ b/ui/res/values-el/strings.xml
@@ -43,8 +43,9 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Διατήρηση"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Ακύρωση"</string>
<string name="retry_download" msgid="7617100787922717912">"Επανάληψη"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Έναρξη τώρα"</string>
<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-en-rAU/strings.xml b/ui/res/values-en-rAU/strings.xml
index 15cab92d..05289ccd 100644
--- a/ui/res/values-en-rAU/strings.xml
+++ b/ui/res/values-en-rAU/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Keep"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Cancel"</string>
<string name="retry_download" msgid="7617100787922717912">"Retry"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Start now"</string>
<string name="deselect_all" msgid="6348198946254776764">"Deselect all"</string>
<string name="select_all" msgid="634074918366265804">"Select all"</string>
<string name="selected_count" msgid="2101564570019753277">"Selected <xliff:g id="NUMBER">%1$d</xliff:g> out of <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-en-rGB/strings.xml b/ui/res/values-en-rGB/strings.xml
index 15cab92d..05289ccd 100644
--- a/ui/res/values-en-rGB/strings.xml
+++ b/ui/res/values-en-rGB/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Keep"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Cancel"</string>
<string name="retry_download" msgid="7617100787922717912">"Retry"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Start now"</string>
<string name="deselect_all" msgid="6348198946254776764">"Deselect all"</string>
<string name="select_all" msgid="634074918366265804">"Select all"</string>
<string name="selected_count" msgid="2101564570019753277">"Selected <xliff:g id="NUMBER">%1$d</xliff:g> out of <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-en-rIN/strings.xml b/ui/res/values-en-rIN/strings.xml
index 15cab92d..05289ccd 100644
--- a/ui/res/values-en-rIN/strings.xml
+++ b/ui/res/values-en-rIN/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Keep"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Cancel"</string>
<string name="retry_download" msgid="7617100787922717912">"Retry"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Start now"</string>
<string name="deselect_all" msgid="6348198946254776764">"Deselect all"</string>
<string name="select_all" msgid="634074918366265804">"Select all"</string>
<string name="selected_count" msgid="2101564570019753277">"Selected <xliff:g id="NUMBER">%1$d</xliff:g> out of <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-es-rUS/strings.xml b/ui/res/values-es-rUS/strings.xml
index cd040690..aad7ad0c 100644
--- a/ui/res/values-es-rUS/strings.xml
+++ b/ui/res/values-es-rUS/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Conservar"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Cancelar"</string>
<string name="retry_download" msgid="7617100787922717912">"Intentar nuevamente"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Comenzar ahora"</string>
<string name="deselect_all" msgid="6348198946254776764">"Desmarcar todas"</string>
<string name="select_all" msgid="634074918366265804">"Seleccionar todas"</string>
<string name="selected_count" msgid="2101564570019753277">"Se seleccionaron <xliff:g id="NUMBER">%1$d</xliff:g> de entre <xliff:g id="TOTAL">%2$d</xliff:g>."</string>
diff --git a/ui/res/values-es/strings.xml b/ui/res/values-es/strings.xml
index 37903dca..c024fbe1 100644
--- a/ui/res/values-es/strings.xml
+++ b/ui/res/values-es/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Conservar"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Cancelar"</string>
<string name="retry_download" msgid="7617100787922717912">"Reintentar"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Iniciar"</string>
<string name="deselect_all" msgid="6348198946254776764">"Desmarcar todo"</string>
<string name="select_all" msgid="634074918366265804">"Seleccionar todo"</string>
<string name="selected_count" msgid="2101564570019753277">"Elegido: <xliff:g id="NUMBER">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-et-rEE/strings.xml b/ui/res/values-et-rEE/strings.xml
index e2602e5d..b4e46c3a 100644
--- a/ui/res/values-et-rEE/strings.xml
+++ b/ui/res/values-et-rEE/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Jäta alles"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Tühista"</string>
<string name="retry_download" msgid="7617100787922717912">"Proovi uuesti"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Alusta kohe"</string>
<string name="deselect_all" msgid="6348198946254776764">"Tühista kõik valikud"</string>
<string name="select_all" msgid="634074918366265804">"Vali kõik"</string>
<string name="selected_count" msgid="2101564570019753277">"Valitud <xliff:g id="NUMBER">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-eu-rES/strings.xml b/ui/res/values-eu-rES/strings.xml
index b852bb0c..9a5c0ba1 100644
--- a/ui/res/values-eu-rES/strings.xml
+++ b/ui/res/values-eu-rES/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Utzi bere horretan"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Utzi"</string>
<string name="retry_download" msgid="7617100787922717912">"Saiatu berriro"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Hasi"</string>
<string name="deselect_all" msgid="6348198946254776764">"Desautatu guztiak"</string>
<string name="select_all" msgid="634074918366265804">"Hautatu guztiak"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="NUMBER">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g> hautatuta"</string>
diff --git a/ui/res/values-fa/strings.xml b/ui/res/values-fa/strings.xml
index 61a9ffbe..e78f900e 100644
--- a/ui/res/values-fa/strings.xml
+++ b/ui/res/values-fa/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"حفظ"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"لغو"</string>
<string name="retry_download" msgid="7617100787922717912">"امتحان مجدد"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"اکنون شروع شود"</string>
<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>
diff --git a/ui/res/values-fi/strings.xml b/ui/res/values-fi/strings.xml
index 7a30abed..6221c527 100644
--- a/ui/res/values-fi/strings.xml
+++ b/ui/res/values-fi/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Säilytä"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Peruuta"</string>
<string name="retry_download" msgid="7617100787922717912">"Yritä uudelleen"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Aloita nyt"</string>
<string name="deselect_all" msgid="6348198946254776764">"Poista kaikki valinnat"</string>
<string name="select_all" msgid="634074918366265804">"Valitse kaikki"</string>
<string name="selected_count" msgid="2101564570019753277">"Valittu <xliff:g id="NUMBER">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-fr-rCA/strings.xml b/ui/res/values-fr-rCA/strings.xml
index 69dacc94..0e732e0e 100644
--- a/ui/res/values-fr-rCA/strings.xml
+++ b/ui/res/values-fr-rCA/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Conserver"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Annuler"</string>
<string name="retry_download" msgid="7617100787922717912">"Réessayer"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Commencer"</string>
<string name="deselect_all" msgid="6348198946254776764">"Tout désélectionner"</string>
<string name="select_all" msgid="634074918366265804">"Tout sélectionner"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="NUMBER">%1$d</xliff:g> téléchargements sélectionnés sur <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-fr/strings.xml b/ui/res/values-fr/strings.xml
index 1305ab5c..9525409c 100644
--- a/ui/res/values-fr/strings.xml
+++ b/ui/res/values-fr/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Conserver"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Annuler"</string>
<string name="retry_download" msgid="7617100787922717912">"Réessayer"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Commencer"</string>
<string name="deselect_all" msgid="6348198946254776764">"Tout désélectionner"</string>
<string name="select_all" msgid="634074918366265804">"Tout sélectionner"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="NUMBER">%1$d</xliff:g> téléchargements sélectionnés sur <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-gl-rES/strings.xml b/ui/res/values-gl-rES/strings.xml
index c3d6c800..841851b5 100644
--- a/ui/res/values-gl-rES/strings.xml
+++ b/ui/res/values-gl-rES/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Manter"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Cancelar"</string>
<string name="retry_download" msgid="7617100787922717912">"Tentar de novo"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Iniciar agora"</string>
<string name="deselect_all" msgid="6348198946254776764">"Desmarcar todo"</string>
<string name="select_all" msgid="634074918366265804">"Seleccionar todas"</string>
<string name="selected_count" msgid="2101564570019753277">"Seleccionáronse <xliff:g id="NUMBER">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-gu-rIN/strings.xml b/ui/res/values-gu-rIN/strings.xml
index 7fefdacc..39bfce8c 100644
--- a/ui/res/values-gu-rIN/strings.xml
+++ b/ui/res/values-gu-rIN/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"રાખો"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"રદ કરો"</string>
<string name="retry_download" msgid="7617100787922717912">"ફરી પ્રયાસ કરો"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"હવે શરૂ કરો"</string>
<string name="deselect_all" msgid="6348198946254776764">"તમામને નાપસંદ કરો"</string>
<string name="select_all" msgid="634074918366265804">"બધા પસંદ કરો"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g> માંથી <xliff:g id="NUMBER">%1$d</xliff:g> પસંદ કર્યા"</string>
diff --git a/ui/res/values-hi/strings.xml b/ui/res/values-hi/strings.xml
index 715df5c5..4a763e1b 100644
--- a/ui/res/values-hi/strings.xml
+++ b/ui/res/values-hi/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"रखें"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"अभी नहीं"</string>
<string name="retry_download" msgid="7617100787922717912">"पुन: प्रयास करें"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"अभी शुरू करें"</string>
<string name="deselect_all" msgid="6348198946254776764">"कुछ भी ना चुनें"</string>
<string name="select_all" msgid="634074918366265804">"सभी चुनें"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g> में से <xliff:g id="NUMBER">%1$d</xliff:g> चयनित"</string>
diff --git a/ui/res/values-hr/strings.xml b/ui/res/values-hr/strings.xml
index 8f07eaef..8ff14ddd 100644
--- a/ui/res/values-hr/strings.xml
+++ b/ui/res/values-hr/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Zadrži"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Odustani"</string>
<string name="retry_download" msgid="7617100787922717912">"Pokušaj ponovo"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Započni odmah"</string>
<string name="deselect_all" msgid="6348198946254776764">"Poništi odabir za sve"</string>
<string name="select_all" msgid="634074918366265804">"Odaberi sve"</string>
<string name="selected_count" msgid="2101564570019753277">"Odabrano <xliff:g id="NUMBER">%1$d</xliff:g> od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-hu/strings.xml b/ui/res/values-hu/strings.xml
index 74145297..aeaf2e04 100644
--- a/ui/res/values-hu/strings.xml
+++ b/ui/res/values-hu/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Megőrzés"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Mégse"</string>
<string name="retry_download" msgid="7617100787922717912">"Újra"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Indítás most"</string>
<string name="deselect_all" msgid="6348198946254776764">"Az összes kijelölés törlése"</string>
<string name="select_all" msgid="634074918366265804">"Összes kijelölése"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g>/<xliff:g id="NUMBER">%1$d</xliff:g> kiválasztva"</string>
diff --git a/ui/res/values-hy-rAM/strings.xml b/ui/res/values-hy-rAM/strings.xml
index 0b494a6c..26354c13 100644
--- a/ui/res/values-hy-rAM/strings.xml
+++ b/ui/res/values-hy-rAM/strings.xml
@@ -43,8 +43,9 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Պահել"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Չեղարկել"</string>
<string name="retry_download" msgid="7617100787922717912">"Կրկնել"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Մեկնարկել հիմա"</string>
<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-in/strings.xml b/ui/res/values-in/strings.xml
index 72cb4372..1c9bf600 100644
--- a/ui/res/values-in/strings.xml
+++ b/ui/res/values-in/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Simpan"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Batal"</string>
<string name="retry_download" msgid="7617100787922717912">"Coba Lagi"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Mulai sekarang"</string>
<string name="deselect_all" msgid="6348198946254776764">"Batalkan semua pilihan"</string>
<string name="select_all" msgid="634074918366265804">"Pilih semua"</string>
<string name="selected_count" msgid="2101564570019753277">"Terpilih <xliff:g id="NUMBER">%1$d</xliff:g> dari <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-is-rIS/strings.xml b/ui/res/values-is-rIS/strings.xml
index 2db65251..304d0b28 100644
--- a/ui/res/values-is-rIS/strings.xml
+++ b/ui/res/values-is-rIS/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Halda"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Hætta við"</string>
<string name="retry_download" msgid="7617100787922717912">"Reyna aftur"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Byrja núna"</string>
<string name="deselect_all" msgid="6348198946254776764">"Velja ekkert"</string>
<string name="select_all" msgid="634074918366265804">"Velja allt"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="NUMBER">%1$d</xliff:g> skrár valdar af <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-it/strings.xml b/ui/res/values-it/strings.xml
index db961fbe..68d8365e 100644
--- a/ui/res/values-it/strings.xml
+++ b/ui/res/values-it/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Conserva"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Annulla"</string>
<string name="retry_download" msgid="7617100787922717912">"Riprova"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Avvia adesso"</string>
<string name="deselect_all" msgid="6348198946254776764">"Deseleziona tutto"</string>
<string name="select_all" msgid="634074918366265804">"Seleziona tutto"</string>
<string name="selected_count" msgid="2101564570019753277">"Selezionati <xliff:g id="NUMBER">%1$d</xliff:g> di <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-iw/strings.xml b/ui/res/values-iw/strings.xml
index d284a462..390e3bcf 100644
--- a/ui/res/values-iw/strings.xml
+++ b/ui/res/values-iw/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"שמור"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"ביטול"</string>
<string name="retry_download" msgid="7617100787922717912">"נסה שוב"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"התחל כעת"</string>
<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>
diff --git a/ui/res/values-ja/strings.xml b/ui/res/values-ja/strings.xml
index 49e62325..56dec4b2 100644
--- a/ui/res/values-ja/strings.xml
+++ b/ui/res/values-ja/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"継続"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"キャンセル"</string>
<string name="retry_download" msgid="7617100787922717912">"再試行"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"今すぐ開始"</string>
<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>
diff --git a/ui/res/values-ka-rGE/strings.xml b/ui/res/values-ka-rGE/strings.xml
index 17dab2af..456a584a 100644
--- a/ui/res/values-ka-rGE/strings.xml
+++ b/ui/res/values-ka-rGE/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"შენარჩუნება"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"გაუქმება"</string>
<string name="retry_download" msgid="7617100787922717912">"გამეორება"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"ახლავე დაწყება"</string>
<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>
diff --git a/ui/res/values-kk-rKZ/strings.xml b/ui/res/values-kk-rKZ/strings.xml
index 061d127d..7ce4213f 100644
--- a/ui/res/values-kk-rKZ/strings.xml
+++ b/ui/res/values-kk-rKZ/strings.xml
@@ -41,8 +41,9 @@
<string name="remove_download" msgid="6372920256257247857">"Алып тастау"</string>
<string name="delete_download" msgid="76629022653866471">"Жою"</string>
<string name="keep_queued_download" msgid="5144882786014818569">"Қалсын"</string>
- <string name="cancel_running_download" msgid="5232704030969221112">"Өшіру"</string>
+ <string name="cancel_running_download" msgid="5232704030969221112">"Жабу"</string>
<string name="retry_download" msgid="7617100787922717912">"Қайта әрекеттену"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Қазір бастау"</string>
<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>
diff --git a/ui/res/values-km-rKH/strings.xml b/ui/res/values-km-rKH/strings.xml
index cc5f49b6..fa8e53c2 100644
--- a/ui/res/values-km-rKH/strings.xml
+++ b/ui/res/values-km-rKH/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"បន្ត"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"បោះ​បង់​"</string>
<string name="retry_download" msgid="7617100787922717912">"សាកល្បង​ម្ដងទៀត"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"ចាប់ផ្ដើមឥឡូវនេះ"</string>
<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>
diff --git a/ui/res/values-kn-rIN/strings.xml b/ui/res/values-kn-rIN/strings.xml
index 62fa8a66..67722747 100644
--- a/ui/res/values-kn-rIN/strings.xml
+++ b/ui/res/values-kn-rIN/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"ಇರಿಸಿ"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"ರದ್ದುಮಾಡು"</string>
<string name="retry_download" msgid="7617100787922717912">"ಮರುಪ್ರಯತ್ನಿಸಿ"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"ಈಗ ಪ್ರಾರಂಭಿಸಿ"</string>
<string name="deselect_all" msgid="6348198946254776764">"ಎಲ್ಲಾ ಆಯ್ಕೆಯನ್ನು ರದ್ದುಮಾಡಿ"</string>
<string name="select_all" msgid="634074918366265804">"ಎಲ್ಲವನ್ನೂ ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="NUMBER">%1$d</xliff:g> ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</string>
diff --git a/ui/res/values-ko/strings.xml b/ui/res/values-ko/strings.xml
index 75e69264..b38b7dc5 100644
--- a/ui/res/values-ko/strings.xml
+++ b/ui/res/values-ko/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"유지"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"취소"</string>
<string name="retry_download" msgid="7617100787922717912">"다시 시도"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"시작하기"</string>
<string name="deselect_all" msgid="6348198946254776764">"모두 선택취소"</string>
<string name="select_all" msgid="634074918366265804">"모두 선택"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g>개 중 <xliff:g id="NUMBER">%1$d</xliff:g>개가 선택됨"</string>
diff --git a/ui/res/values-ky-rKG/strings.xml b/ui/res/values-ky-rKG/strings.xml
index ccfdf0ca..d20c7c09 100644
--- a/ui/res/values-ky-rKG/strings.xml
+++ b/ui/res/values-ky-rKG/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>
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Калтыруу"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Айнуу"</string>
<string name="retry_download" msgid="7617100787922717912">"Кайра аракеттенүү"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Азыр баштоо"</string>
<string name="deselect_all" msgid="6348198946254776764">"Баарын бошотуу"</string>
<string name="select_all" msgid="634074918366265804">"Бардыгын тандоо"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g> ичинен <xliff:g id="NUMBER">%1$d</xliff:g> тандалды"</string>
diff --git a/ui/res/values-lo-rLA/strings.xml b/ui/res/values-lo-rLA/strings.xml
index 70eb8d67..b62a203b 100644
--- a/ui/res/values-lo-rLA/strings.xml
+++ b/ui/res/values-lo-rLA/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"ເກັບໄວ້"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"ຍົກເລີກ"</string>
<string name="retry_download" msgid="7617100787922717912">"ລອງໃໝ່"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"ເລີ່ມດຽວນີ້"</string>
<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>
diff --git a/ui/res/values-lt/strings.xml b/ui/res/values-lt/strings.xml
index 0013210d..f41b4e9c 100644
--- a/ui/res/values-lt/strings.xml
+++ b/ui/res/values-lt/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Palikti"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Atšaukti"</string>
<string name="retry_download" msgid="7617100787922717912">"Band. dar k."</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Pradėti dabar"</string>
<string name="deselect_all" msgid="6348198946254776764">"Panaikinti visus žymėjim."</string>
<string name="select_all" msgid="634074918366265804">"Pasirinkti viską"</string>
<string name="selected_count" msgid="2101564570019753277">"Pasirinkti (-a) <xliff:g id="NUMBER">%1$d</xliff:g> iš <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-lv/strings.xml b/ui/res/values-lv/strings.xml
index 6e32b86b..7187bd93 100644
--- a/ui/res/values-lv/strings.xml
+++ b/ui/res/values-lv/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Paturēt"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Atcelt"</string>
<string name="retry_download" msgid="7617100787922717912">"Mēģināt vēlreiz"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Sākt tūlīt"</string>
<string name="deselect_all" msgid="6348198946254776764">"Atcelt visu atlasi"</string>
<string name="select_all" msgid="634074918366265804">"Atlasīt visu"</string>
<string name="selected_count" msgid="2101564570019753277">"Atlasītas <xliff:g id="NUMBER">%1$d</xliff:g> no <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-mk-rMK/strings.xml b/ui/res/values-mk-rMK/strings.xml
index 15951642..cb52bcd6 100644
--- a/ui/res/values-mk-rMK/strings.xml
+++ b/ui/res/values-mk-rMK/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Задржи"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Откажи"</string>
<string name="retry_download" msgid="7617100787922717912">"Обиди се повторно"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Започни сега"</string>
<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>
diff --git a/ui/res/values-ml-rIN/strings.xml b/ui/res/values-ml-rIN/strings.xml
index 29f9a70e..6b3e89c0 100644
--- a/ui/res/values-ml-rIN/strings.xml
+++ b/ui/res/values-ml-rIN/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>
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"സൂക്ഷിക്കുക"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"റദ്ദാക്കുക"</string>
<string name="retry_download" msgid="7617100787922717912">"വീണ്ടും ശ്രമിക്കുക"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"ഇപ്പോൾ ആരംഭിക്കുക"</string>
<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>
diff --git a/ui/res/values-mn-rMN/strings.xml b/ui/res/values-mn-rMN/strings.xml
index 3890a24f..a6608905 100644
--- a/ui/res/values-mn-rMN/strings.xml
+++ b/ui/res/values-mn-rMN/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Байлгах"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Цуцлах"</string>
<string name="retry_download" msgid="7617100787922717912">"Дахин оролдох"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Одоо эхлүүлэх"</string>
<string name="deselect_all" msgid="6348198946254776764">"Бүгдийг сонгохгүй"</string>
<string name="select_all" msgid="634074918366265804">"Бүгдийг сонгох"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g>-с <xliff:g id="NUMBER">%1$d</xliff:g> нь сонгогдсон"</string>
diff --git a/ui/res/values-mr-rIN/strings.xml b/ui/res/values-mr-rIN/strings.xml
index 4340c570..433fed54 100644
--- a/ui/res/values-mr-rIN/strings.xml
+++ b/ui/res/values-mr-rIN/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"ठेवा"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"रद्द करा"</string>
<string name="retry_download" msgid="7617100787922717912">"पुन्हा प्रयत्न करा"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"आता प्रारंभ करा"</string>
<string name="deselect_all" msgid="6348198946254776764">"सर्व निवड रद्द करा"</string>
<string name="select_all" msgid="634074918366265804">"सर्व निवडा"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g> पैकी <xliff:g id="NUMBER">%1$d</xliff:g> निवडले"</string>
diff --git a/ui/res/values-ms-rMY/strings.xml b/ui/res/values-ms-rMY/strings.xml
index e1e9f666..c7103b87 100644
--- a/ui/res/values-ms-rMY/strings.xml
+++ b/ui/res/values-ms-rMY/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Simpan"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Batal"</string>
<string name="retry_download" msgid="7617100787922717912">"Cuba semula"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Mulakan sekarang"</string>
<string name="deselect_all" msgid="6348198946254776764">"Nyahpilih semua"</string>
<string name="select_all" msgid="634074918366265804">"Pilih semua"</string>
<string name="selected_count" msgid="2101564570019753277">"Pilihan <xliff:g id="NUMBER">%1$d</xliff:g> dari <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-my-rMM/strings.xml b/ui/res/values-my-rMM/strings.xml
index f2483e8e..5d286723 100644
--- a/ui/res/values-my-rMM/strings.xml
+++ b/ui/res/values-my-rMM/strings.xml
@@ -16,9 +16,9 @@
<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="download_title_sorted_by_date" msgid="5898014492155434221">"ဒေါင်းလုပ်များ -နေ့စွဲအလိုက် စီထားသည်"</string>
- <string name="download_title_sorted_by_size" msgid="1417193166677094813">"ဒေါင်းလုပ်များ -အရွယ်အစားအလိုက် စီထားသည်"</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>
<string name="missing_title" msgid="830115697868833773">"အကြောင်းအရာ မသိရှိ"</string>
<string name="button_sort_by_size" msgid="7331549713691146251">"အရွယ်အစားအလိုက် စီရန်"</string>
@@ -32,17 +32,18 @@
<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_external" msgid="8692452156251449195">"ဒေါင်းလုဒ် မပြီးဆုံးနိုင်ပါ၊ ပြင်ပ သိုလှောင်ရာနေရာတွင် လုံလောက်သော နေရာလွတ် မရှိပါ။"</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>
+ <string name="dialog_media_not_found" msgid="4468088418758018765">"ဒေါင်းလုဒ် မပြုလုပ်နိုင်ပါ၊ ပြင်ပ မီဒီယာ မရှိပါ။"</string>
<string name="download_no_application_title" msgid="7024782176657362251">"ဖိုင်အား ဖွင့်မရပါ"</string>
<string name="remove_download" msgid="6372920256257247857">"ဖယ်ရှားရန်"</string>
<string name="delete_download" msgid="76629022653866471">"ဖျက်ပစ်ရန်"</string>
<string name="keep_queued_download" msgid="5144882786014818569">"သိမ်းထားပါ"</string>
- <string name="cancel_running_download" msgid="5232704030969221112">"ထားတော့"</string>
+ <string name="cancel_running_download" msgid="5232704030969221112">"မလုပ်တော့ပါ"</string>
<string name="retry_download" msgid="7617100787922717912">"ပြန်ကြိုးစားပါ"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"ယခု စတင်ပါ"</string>
<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>
diff --git a/ui/res/values-nb/strings.xml b/ui/res/values-nb/strings.xml
index 4a0faa6c..143fa765 100644
--- a/ui/res/values-nb/strings.xml
+++ b/ui/res/values-nb/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Behold"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Avbryt"</string>
<string name="retry_download" msgid="7617100787922717912">"Prøv på nytt"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Start nå"</string>
<string name="deselect_all" msgid="6348198946254776764">"Opphev alle markeringer"</string>
<string name="select_all" msgid="634074918366265804">"Velg alle"</string>
<string name="selected_count" msgid="2101564570019753277">"Du har valgt <xliff:g id="NUMBER">%1$d</xliff:g> av <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-ne-rNP/strings.xml b/ui/res/values-ne-rNP/strings.xml
index 2a7389b2..f9966a56 100644
--- a/ui/res/values-ne-rNP/strings.xml
+++ b/ui/res/values-ne-rNP/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"राख्नुहोस्"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"रद्द गर्नुहोस्"</string>
<string name="retry_download" msgid="7617100787922717912">"पुनःप्रयास गर्नुहोस्"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"अहिले सुरु गर्नुहोस्"</string>
<string name="deselect_all" msgid="6348198946254776764">"सबै अचयन गर्नुहोस्"</string>
<string name="select_all" msgid="634074918366265804">"सबै चयन गर्नुहोस्"</string>
<string name="selected_count" msgid="2101564570019753277">"छानिएको <xliff:g id="TOTAL">%2$d</xliff:g> को <xliff:g id="NUMBER">%1$d</xliff:g> बाट"</string>
diff --git a/ui/res/values-nl/strings.xml b/ui/res/values-nl/strings.xml
index 47d063c7..41efab11 100644
--- a/ui/res/values-nl/strings.xml
+++ b/ui/res/values-nl/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Houden"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Annuleren"</string>
<string name="retry_download" msgid="7617100787922717912">"Opnieuw proberen"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Nu starten"</string>
<string name="deselect_all" msgid="6348198946254776764">"Alle selecties opheffen"</string>
<string name="select_all" msgid="634074918366265804">"Alles selecteren"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="NUMBER">%1$d</xliff:g> van <xliff:g id="TOTAL">%2$d</xliff:g> geselecteerd"</string>
diff --git a/ui/res/values-pa-rIN/strings.xml b/ui/res/values-pa-rIN/strings.xml
index e32011b1..4d26bb49 100644
--- a/ui/res/values-pa-rIN/strings.xml
+++ b/ui/res/values-pa-rIN/strings.xml
@@ -16,10 +16,10 @@
<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>
+ <string name="no_downloads" msgid="1029667411186146836">"ਕੋਈ ਡਾਊਨਲੋਡ ਨਹੀਂ।"</string>
<string name="missing_title" msgid="830115697868833773">"&lt;ਅਗਿਆਤ&gt;"</string>
<string name="button_sort_by_size" msgid="7331549713691146251">"ਆਕਾਰ ਮੁਤਾਬਕ ਛਾਂਟੋ"</string>
<string name="button_sort_by_date" msgid="8800842892684101528">"ਤਾਰੀਖ ਮੁਤਾਬਕ ਛਾਂਟੋ"</string>
@@ -43,8 +43,9 @@
<string name="keep_queued_download" msgid="5144882786014818569">"ਰੱਖੋ"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"ਰੱਦ ਕਰੋ"</string>
<string name="retry_download" msgid="7617100787922717912">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"ਹੁਣ ਸ਼ੁਰੂ ਕਰੋ"</string>
<string name="deselect_all" msgid="6348198946254776764">"ਸਾਰਿਆਂ ਨੂੰ ਅਚੋਣਵਾਂ ਕਰੋ"</string>
- <string name="select_all" msgid="634074918366265804">"ਸਾਰੇ ਚੁਣੋ"</string>
+ <string name="select_all" msgid="634074918366265804">"ਸਭ ਚੁਣੋ"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g> ਵਿੱਚੋਂ <xliff:g id="NUMBER">%1$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-pl/strings.xml b/ui/res/values-pl/strings.xml
index 93c33f67..160faf53 100644
--- a/ui/res/values-pl/strings.xml
+++ b/ui/res/values-pl/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Zachowaj"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Anuluj"</string>
<string name="retry_download" msgid="7617100787922717912">"Ponów próbę"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Rozpocznij teraz"</string>
<string name="deselect_all" msgid="6348198946254776764">"Usuń zaznaczenie wszystkich"</string>
<string name="select_all" msgid="634074918366265804">"Zaznacz wszystkie"</string>
<string name="selected_count" msgid="2101564570019753277">"Wybrano <xliff:g id="NUMBER">%1$d</xliff:g> z <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-pt-rPT/strings.xml b/ui/res/values-pt-rPT/strings.xml
index cdfa94f9..c0bb20fe 100644
--- a/ui/res/values-pt-rPT/strings.xml
+++ b/ui/res/values-pt-rPT/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Manter"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Cancelar"</string>
<string name="retry_download" msgid="7617100787922717912">"Tentar novamente"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Iniciar agora"</string>
<string name="deselect_all" msgid="6348198946254776764">"Desmarcar tudo"</string>
<string name="select_all" msgid="634074918366265804">"Selecionar tudo"</string>
<string name="selected_count" msgid="2101564570019753277">"Selecionado(s) <xliff:g id="NUMBER">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-pt/strings.xml b/ui/res/values-pt/strings.xml
index 53052c90..2c2add42 100644
--- a/ui/res/values-pt/strings.xml
+++ b/ui/res/values-pt/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Manter"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Cancelar"</string>
<string name="retry_download" msgid="7617100787922717912">"Tentar novamente"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Iniciar agora"</string>
<string name="deselect_all" msgid="6348198946254776764">"Desmarcar tudo"</string>
<string name="select_all" msgid="634074918366265804">"Selecionar tudo"</string>
<string name="selected_count" msgid="2101564570019753277">"Selecionados <xliff:g id="NUMBER">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-ro/strings.xml b/ui/res/values-ro/strings.xml
index b387e449..6dd16149 100644
--- a/ui/res/values-ro/strings.xml
+++ b/ui/res/values-ro/strings.xml
@@ -21,30 +21,31 @@
<string name="download_title_sorted_by_size" msgid="1417193166677094813">"Descărcări – sortate după dimensiune"</string>
<string name="no_downloads" msgid="1029667411186146836">"Nu există descărcări."</string>
<string name="missing_title" msgid="830115697868833773">"&lt;Necunoscut&gt;"</string>
- <string name="button_sort_by_size" msgid="7331549713691146251">"Sortaţi după dimensiune"</string>
- <string name="button_sort_by_date" msgid="8800842892684101528">"Sortaţi după dată"</string>
- <string name="download_queued" msgid="104973307780629904">"În aştept."</string>
+ <string name="button_sort_by_size" msgid="7331549713691146251">"Sortați după dimensiune"</string>
+ <string name="button_sort_by_date" msgid="8800842892684101528">"Sortați după dată"</string>
+ <string name="download_queued" msgid="104973307780629904">"În aștept."</string>
<string name="download_running" msgid="4656462962155580641">"În curs"</string>
<string name="download_success" msgid="7006048006543495236">"Finalizat"</string>
<string name="download_error" msgid="8081329546008568251">"Nereușită"</string>
<string name="dialog_title_not_available" msgid="5746317632356158515">"Nu s-a putut descărca"</string>
- <string name="dialog_failed_body" msgid="587545111677064427">"Doriți să reîncercați descărcarea fişierului mai târziu sau să îl ștergeți din lista de așteptare?"</string>
+ <string name="dialog_failed_body" msgid="587545111677064427">"Doriți să reîncercați descărcarea fișierului mai târziu sau să îl ștergeți din lista de așteptare?"</string>
<string name="dialog_title_queued_body" msgid="6760681913815015219">"Fișier în coadă"</string>
- <string name="dialog_queued_body" msgid="708552801635572720">"Fişierul este adăugat în coadă pentru descărcare ulterioară, prin urmare nu este disponibil încă."</string>
- <string name="dialog_file_missing_body" msgid="3223012612774276284">"Nu se poate găsi fişierul descărcat."</string>
- <string name="dialog_insufficient_space_on_external" msgid="8692452156251449195">"Nu se poate finaliza descărcarea. Nu există suficient spaţiu de stocare extern."</string>
- <string name="dialog_insufficient_space_on_cache" msgid="6313630206163908994">"Nu se poate finaliza descărcarea. Nu există suficient spaţiu de stocare intern."</string>
+ <string name="dialog_queued_body" msgid="708552801635572720">"Fișierul este adăugat în coadă pentru descărcare ulterioară, prin urmare nu este disponibil încă."</string>
+ <string name="dialog_file_missing_body" msgid="3223012612774276284">"Nu se poate găsi fișierul descărcat."</string>
+ <string name="dialog_insufficient_space_on_external" msgid="8692452156251449195">"Nu se poate finaliza descărcarea. Nu există suficient spațiu de stocare extern."</string>
+ <string name="dialog_insufficient_space_on_cache" msgid="6313630206163908994">"Nu se poate finaliza descărcarea. Nu există suficient spațiu de stocare intern."</string>
<string name="dialog_cannot_resume" msgid="8664509751358983543">"Descărcarea a fost întreruptă și nu poate fi reluată."</string>
- <string name="dialog_file_already_exists" msgid="8308563940663449590">"Nu se poate descărca. Fişierul de destinaţie există deja."</string>
+ <string name="dialog_file_already_exists" msgid="8308563940663449590">"Nu se poate descărca. Fișierul de destinație există deja."</string>
<string name="dialog_media_not_found" msgid="4468088418758018765">"Nu se poate descărca. Nu este disponibil un mediu de stocare extern."</string>
- <string name="download_no_application_title" msgid="7024782176657362251">"Fişierul nu poate fi deschis"</string>
- <string name="remove_download" msgid="6372920256257247857">"Eliminaţi"</string>
- <string name="delete_download" msgid="76629022653866471">"Ștergeţi"</string>
- <string name="keep_queued_download" msgid="5144882786014818569">"Păstraţi"</string>
+ <string name="download_no_application_title" msgid="7024782176657362251">"Fișierul nu poate fi deschis"</string>
+ <string name="remove_download" msgid="6372920256257247857">"Eliminați"</string>
+ <string name="delete_download" msgid="76629022653866471">"Ștergeți"</string>
+ <string name="keep_queued_download" msgid="5144882786014818569">"Păstrați"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Anulați"</string>
<string name="retry_download" msgid="7617100787922717912">"Reîncercați"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Începeți acum"</string>
<string name="deselect_all" msgid="6348198946254776764">"Deselectați-le pe toate"</string>
- <string name="select_all" msgid="634074918366265804">"Selectaţi-le pe toate"</string>
+ <string name="select_all" msgid="634074918366265804">"Selectați-le pe toate"</string>
<string name="selected_count" msgid="2101564570019753277">"Au fost selectate <xliff:g id="NUMBER">%1$d</xliff:g> din <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
- <string name="download_share_dialog" msgid="3355867339806448955">"Distribuiţi prin"</string>
+ <string name="download_share_dialog" msgid="3355867339806448955">"Distribuiți prin"</string>
</resources>
diff --git a/ui/res/values-ru/strings.xml b/ui/res/values-ru/strings.xml
index 2691941c..c60dba59 100644
--- a/ui/res/values-ru/strings.xml
+++ b/ui/res/values-ru/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Сохранить"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Отмена"</string>
<string name="retry_download" msgid="7617100787922717912">"Повторить"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Начать"</string>
<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>
diff --git a/ui/res/values-si-rLK/strings.xml b/ui/res/values-si-rLK/strings.xml
index 11873878..d8e59830 100644
--- a/ui/res/values-si-rLK/strings.xml
+++ b/ui/res/values-si-rLK/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"තබාගැනීම"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"අවලංගු කරන්න"</string>
<string name="retry_download" msgid="7617100787922717912">"නැවත උත්සාහ කරන්න"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"දැන් අරඹන්න"</string>
<string name="deselect_all" msgid="6348198946254776764">"සියල්ල තේරීම අත්හරින්න"</string>
<string name="select_all" msgid="634074918366265804">"සියල්ල තෝරන්න"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g> ගෙන් <xliff:g id="NUMBER">%1$d</xliff:g> ක් තෝරා ගන්නා ලදී"</string>
diff --git a/ui/res/values-sk/strings.xml b/ui/res/values-sk/strings.xml
index 5e19134f..75f9d01d 100644
--- a/ui/res/values-sk/strings.xml
+++ b/ui/res/values-sk/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Zachovať"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Zrušiť"</string>
<string name="retry_download" msgid="7617100787922717912">"Skúsiť znova"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Spustiť"</string>
<string name="deselect_all" msgid="6348198946254776764">"Zrušiť výber všetkého"</string>
<string name="select_all" msgid="634074918366265804">"Vybrať všetko"</string>
<string name="selected_count" msgid="2101564570019753277">"Vybraté: <xliff:g id="NUMBER">%1$d</xliff:g> z <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-sl/strings.xml b/ui/res/values-sl/strings.xml
index c506a9de..0154c377 100644
--- a/ui/res/values-sl/strings.xml
+++ b/ui/res/values-sl/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Obdrži"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Prekliči"</string>
<string name="retry_download" msgid="7617100787922717912">"Poskusi znova"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Začni zdaj"</string>
<string name="deselect_all" msgid="6348198946254776764">"Prekliči celoten izbor"</string>
<string name="select_all" msgid="634074918366265804">"Izberi vse"</string>
<string name="selected_count" msgid="2101564570019753277">"Izbrano: <xliff:g id="NUMBER">%1$d</xliff:g> od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-sq-rAL/strings.xml b/ui/res/values-sq-rAL/strings.xml
index 4860d5d1..9fe520ef 100644
--- a/ui/res/values-sq-rAL/strings.xml
+++ b/ui/res/values-sq-rAL/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Mbaje"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Anulo"</string>
<string name="retry_download" msgid="7617100787922717912">"Provo përsëri"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Fillo tani"</string>
<string name="deselect_all" msgid="6348198946254776764">"Hiq përzgjedhjen nga të gjitha"</string>
<string name="select_all" msgid="634074918366265804">"Përzgjidhi të gjitha"</string>
<string name="selected_count" msgid="2101564570019753277">"U zgjodhën <xliff:g id="NUMBER">%1$d</xliff:g> nga gjithsej <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-sr/strings.xml b/ui/res/values-sr/strings.xml
index d077cb5a..33dcfe5e 100644
--- a/ui/res/values-sr/strings.xml
+++ b/ui/res/values-sr/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Задржи"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Откажи"</string>
<string name="retry_download" msgid="7617100787922717912">"Покушај поново"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Започни одмах"</string>
<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>
diff --git a/ui/res/values-sv/strings.xml b/ui/res/values-sv/strings.xml
index 446b8b6f..f53a3ce5 100644
--- a/ui/res/values-sv/strings.xml
+++ b/ui/res/values-sv/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Behåll"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Avbryt"</string>
<string name="retry_download" msgid="7617100787922717912">"Försök igen"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Starta nu"</string>
<string name="deselect_all" msgid="6348198946254776764">"Avmarkera alla"</string>
<string name="select_all" msgid="634074918366265804">"Markera alla"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="NUMBER">%1$d</xliff:g> av <xliff:g id="TOTAL">%2$d</xliff:g> har valts"</string>
diff --git a/ui/res/values-sw/strings.xml b/ui/res/values-sw/strings.xml
index 3e09e8fe..8a42448e 100644
--- a/ui/res/values-sw/strings.xml
+++ b/ui/res/values-sw/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Weka"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Ghairi"</string>
<string name="retry_download" msgid="7617100787922717912">"Jaribu tena"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Anza sasa"</string>
<string name="deselect_all" msgid="6348198946254776764">"Batilisha uteuzi kwa zote"</string>
<string name="select_all" msgid="634074918366265804">"Chagua zote"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="NUMBER">%1$d</xliff:g> zimechaguliwa kati ya <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-ta-rIN/strings.xml b/ui/res/values-ta-rIN/strings.xml
index 7056356c..1fb33ee8 100644
--- a/ui/res/values-ta-rIN/strings.xml
+++ b/ui/res/values-ta-rIN/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"வைத்திரு"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"ரத்துசெய்"</string>
<string name="retry_download" msgid="7617100787922717912">"மீண்டும் முயற்சிசெய்க"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"இப்போதே தொடங்கு"</string>
<string name="deselect_all" msgid="6348198946254776764">"எல்லாம் தேர்வுநீக்கு"</string>
<string name="select_all" msgid="634074918366265804">"எல்லாவற்றையும் தேர்ந்தெடு"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g> இல் <xliff:g id="NUMBER">%1$d</xliff:g> தேர்ந்தெடுக்கப்பட்டன"</string>
diff --git a/ui/res/values-te-rIN/strings.xml b/ui/res/values-te-rIN/strings.xml
index 9099044e..737a7c8f 100644
--- a/ui/res/values-te-rIN/strings.xml
+++ b/ui/res/values-te-rIN/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"ఉంచు"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"రద్దు చేయి"</string>
<string name="retry_download" msgid="7617100787922717912">"మళ్లీ ప్రయత్నించు"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"ఇప్పుడే ప్రారంభించు"</string>
<string name="deselect_all" msgid="6348198946254776764">"అన్నింటి ఎంపికను తీసివేయి"</string>
<string name="select_all" msgid="634074918366265804">"అన్నీ ఎంచుకోండి"</string>
<string name="selected_count" msgid="2101564570019753277">"<xliff:g id="TOTAL">%2$d</xliff:g>లో <xliff:g id="NUMBER">%1$d</xliff:g> ఎంచుకోబడ్డాయి"</string>
diff --git a/ui/res/values-th/strings.xml b/ui/res/values-th/strings.xml
index d48c0841..d12bcf0e 100644
--- a/ui/res/values-th/strings.xml
+++ b/ui/res/values-th/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"เก็บไว้"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"ยกเลิก"</string>
<string name="retry_download" msgid="7617100787922717912">"ลองอีกครั้ง"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"เริ่มเลย"</string>
<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>
diff --git a/ui/res/values-tl/strings.xml b/ui/res/values-tl/strings.xml
index 90f9584e..259b7927 100644
--- a/ui/res/values-tl/strings.xml
+++ b/ui/res/values-tl/strings.xml
@@ -32,17 +32,18 @@
<string name="dialog_title_queued_body" msgid="6760681913815015219">"File sa queue"</string>
<string name="dialog_queued_body" msgid="708552801635572720">"Ang file na ito ay naka-queue para sa pag-download sa hinaharap kaya hindi pa available."</string>
<string name="dialog_file_missing_body" msgid="3223012612774276284">"Hindi matagpuan ang na-download na file."</string>
- <string name="dialog_insufficient_space_on_external" msgid="8692452156251449195">"Hindi matapos ang pag-download. Walang sapat na puwang sa panlabas na storage."</string>
- <string name="dialog_insufficient_space_on_cache" msgid="6313630206163908994">"Hindi matapos ang pag-download. Walang sapat na puwang sa panloob na storage ng pag-download."</string>
+ <string name="dialog_insufficient_space_on_external" msgid="8692452156251449195">"Hindi matapos ang pag-download. Walang sapat na puwang sa external storage."</string>
+ <string name="dialog_insufficient_space_on_cache" msgid="6313630206163908994">"Hindi matapos ang pag-download. Walang sapat na puwang sa internal storage ng pag-download."</string>
<string name="dialog_cannot_resume" msgid="8664509751358983543">"Naantala ang pag-download at hindi maipagpapatuloy."</string>
<string name="dialog_file_already_exists" msgid="8308563940663449590">"Hindi ma-download. Umiiral na ang destination file."</string>
<string name="dialog_media_not_found" msgid="4468088418758018765">"Hindi makapag-download. Hindi available ang panlabas na media."</string>
<string name="download_no_application_title" msgid="7024782176657362251">"Hindi mabuksan ang file"</string>
<string name="remove_download" msgid="6372920256257247857">"Alisin"</string>
- <string name="delete_download" msgid="76629022653866471">"Tanggalin"</string>
+ <string name="delete_download" msgid="76629022653866471">"I-delete"</string>
<string name="keep_queued_download" msgid="5144882786014818569">"Panatilihin"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Kanselahin"</string>
<string name="retry_download" msgid="7617100787922717912">"Subukang muli"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Magsimula ngayon"</string>
<string name="deselect_all" msgid="6348198946254776764">"Alisin sa pagkakapili lahat"</string>
<string name="select_all" msgid="634074918366265804">"Piliin lahat"</string>
<string name="selected_count" msgid="2101564570019753277">"Pinili <xliff:g id="NUMBER">%1$d</xliff:g> sa <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-tr/strings.xml b/ui/res/values-tr/strings.xml
index ba2d64d5..429b06e6 100644
--- a/ui/res/values-tr/strings.xml
+++ b/ui/res/values-tr/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Sakla"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"İptal"</string>
<string name="retry_download" msgid="7617100787922717912">"Tekrar Dene"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Şimdi başlat"</string>
<string name="deselect_all" msgid="6348198946254776764">"Tümünün seçimini kaldır"</string>
<string name="select_all" msgid="634074918366265804">"Tümünü seç"</string>
<string name="selected_count" msgid="2101564570019753277">"Toplam <xliff:g id="TOTAL">%2$d</xliff:g> indirmeden <xliff:g id="NUMBER">%1$d</xliff:g> tanesi seçildi"</string>
diff --git a/ui/res/values-uk/strings.xml b/ui/res/values-uk/strings.xml
index 88b260b9..dae9bd98 100644
--- a/ui/res/values-uk/strings.xml
+++ b/ui/res/values-uk/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Залишити"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Скасувати"</string>
<string name="retry_download" msgid="7617100787922717912">"Повторити"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Почати"</string>
<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>
diff --git a/ui/res/values-ur-rPK/strings.xml b/ui/res/values-ur-rPK/strings.xml
index f7a2feaa..15648481 100644
--- a/ui/res/values-ur-rPK/strings.xml
+++ b/ui/res/values-ur-rPK/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"رکھیں"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"منسوخ کریں"</string>
<string name="retry_download" msgid="7617100787922717912">"دوبارہ کوشش کریں"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"ابھی شروع کریں"</string>
<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>
diff --git a/ui/res/values-uz-rUZ/strings.xml b/ui/res/values-uz-rUZ/strings.xml
index 10930124..d8ed1685 100644
--- a/ui/res/values-uz-rUZ/strings.xml
+++ b/ui/res/values-uz-rUZ/strings.xml
@@ -16,10 +16,10 @@
<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">"Yuklanishlar"</string>
- <string name="download_title_sorted_by_date" msgid="5898014492155434221">"Yuklab olishlar - Sana bo‘yicha saralan."</string>
- <string name="download_title_sorted_by_size" msgid="1417193166677094813">"Yuklab olishlar - Hajm bo‘yicha saralan."</string>
- <string name="no_downloads" msgid="1029667411186146836">"Yuklab olishlar yo‘q."</string>
+ <string name="app_label" msgid="3070921713463294774">"Yuklanmalar"</string>
+ <string name="download_title_sorted_by_date" msgid="5898014492155434221">"Yuklanmalar – sanasi bo‘yicha"</string>
+ <string name="download_title_sorted_by_size" msgid="1417193166677094813">"Yuklanmalar – hajmi bo‘yicha"</string>
+ <string name="no_downloads" msgid="1029667411186146836">"Yuklanmalar yo‘q."</string>
<string name="missing_title" msgid="830115697868833773">"&lt;Noma’lum&gt;"</string>
<string name="button_sort_by_size" msgid="7331549713691146251">"Hajmi bo‘yicha saralash"</string>
<string name="button_sort_by_date" msgid="8800842892684101528">"Sanasi bo‘yicha saralash"</string>
@@ -38,13 +38,14 @@
<string name="dialog_file_already_exists" msgid="8308563940663449590">"Yuklab olinmadi. Mo‘ljaldagi fayl allaqachon mavjud."</string>
<string name="dialog_media_not_found" msgid="4468088418758018765">"Yuklab olinmadi. Tashqi xotira qurilmasi mavjud emas."</string>
<string name="download_no_application_title" msgid="7024782176657362251">"Fayl ochilmadi"</string>
- <string name="remove_download" msgid="6372920256257247857">"O‘chirish"</string>
+ <string name="remove_download" msgid="6372920256257247857">"Olib tashlash"</string>
<string name="delete_download" msgid="76629022653866471">"O‘chirish"</string>
<string name="keep_queued_download" msgid="5144882786014818569">"Saqlash"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Bekor qilish"</string>
<string name="retry_download" msgid="7617100787922717912">"Qayta urinish"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Boshlash"</string>
<string name="deselect_all" msgid="6348198946254776764">"Belgilashlarni bekor qil."</string>
- <string name="select_all" msgid="634074918366265804">"Barchasini belgilash"</string>
+ <string name="select_all" msgid="634074918366265804">"Hammasini belgilash"</string>
<string name="selected_count" msgid="2101564570019753277">"Tanlandi: <xliff:g id="NUMBER">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
- <string name="download_share_dialog" msgid="3355867339806448955">"Ulashish usuli"</string>
+ <string name="download_share_dialog" msgid="3355867339806448955">"Baham ko‘rish"</string>
</resources>
diff --git a/ui/res/values-vi/strings.xml b/ui/res/values-vi/strings.xml
index 040245c0..77597861 100644
--- a/ui/res/values-vi/strings.xml
+++ b/ui/res/values-vi/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Giữ"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Hủy"</string>
<string name="retry_download" msgid="7617100787922717912">"Thử lại"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Bắt đầu ngay"</string>
<string name="deselect_all" msgid="6348198946254776764">"Bỏ chọn tất cả"</string>
<string name="select_all" msgid="634074918366265804">"Chọn tất cả"</string>
<string name="selected_count" msgid="2101564570019753277">"Đã chọn <xliff:g id="NUMBER">%1$d</xliff:g> trong tổng số <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values-zh-rCN/strings.xml b/ui/res/values-zh-rCN/strings.xml
index ae003361..3d0f948d 100644
--- a/ui/res/values-zh-rCN/strings.xml
+++ b/ui/res/values-zh-rCN/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"保留"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"取消"</string>
<string name="retry_download" msgid="7617100787922717912">"重试"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"立即开始"</string>
<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>
diff --git a/ui/res/values-zh-rHK/strings.xml b/ui/res/values-zh-rHK/strings.xml
index c8cd5777..8b2a423c 100644
--- a/ui/res/values-zh-rHK/strings.xml
+++ b/ui/res/values-zh-rHK/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"保留"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"取消"</string>
<string name="retry_download" msgid="7617100787922717912">"重試"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"立即開始"</string>
<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>
diff --git a/ui/res/values-zh-rTW/strings.xml b/ui/res/values-zh-rTW/strings.xml
index ee96b6f4..3d39299c 100644
--- a/ui/res/values-zh-rTW/strings.xml
+++ b/ui/res/values-zh-rTW/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"保留"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"取消"</string>
<string name="retry_download" msgid="7617100787922717912">"重試"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"立即開始"</string>
<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>
diff --git a/ui/res/values-zu/strings.xml b/ui/res/values-zu/strings.xml
index 52347ae9..bcebece4 100644
--- a/ui/res/values-zu/strings.xml
+++ b/ui/res/values-zu/strings.xml
@@ -43,6 +43,7 @@
<string name="keep_queued_download" msgid="5144882786014818569">"Gcina"</string>
<string name="cancel_running_download" msgid="5232704030969221112">"Khansela"</string>
<string name="retry_download" msgid="7617100787922717912">"Zama futhi"</string>
+ <string name="start_now_download" msgid="1564642872809509681">"Qala manje"</string>
<string name="deselect_all" msgid="6348198946254776764">"Ungakhethi konke"</string>
<string name="select_all" msgid="634074918366265804">"Khetha konke"</string>
<string name="selected_count" msgid="2101564570019753277">"Khethiwe <xliff:g id="NUMBER">%1$d</xliff:g> kulezi <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
diff --git a/ui/res/values/strings.xml b/ui/res/values/strings.xml
index 9e625ac7..3555e236 100644
--- a/ui/res/values/strings.xml
+++ b/ui/res/values/strings.xml
@@ -106,6 +106,10 @@
<!-- Text for button appearing in a dialog to restart a download, either one that failed or one
for which the downloaded file is now missing [CHAR LIMIT=25] -->
<string name="retry_download">Retry</string>
+ <!-- Text for button to start a download over the mobile connection now, even though it's over
+ the carrier-specified recommended maximum size for downloads over the mobile connection
+ [CHAR LIMIT=25] -->
+ <string name="start_now_download">Start now</string>
<!-- Text for button appearing in the pop-up selection menu to deselect all currently selected
downloads in the download list [CHAR LIMIT=25] -->
<string name="deselect_all">Deselect all</string>
diff --git a/ui/src/com/android/providers/downloads/ui/DownloadList.java b/ui/src/com/android/providers/downloads/ui/DownloadList.java
deleted file mode 100644
index 044bd4eb..00000000
--- a/ui/src/com/android/providers/downloads/ui/DownloadList.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2010 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.content.Intent;
-import android.os.Bundle;
-import android.provider.DocumentsContract;
-
-import com.android.providers.downloads.Constants;
-
-public class DownloadList extends Activity {
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- // Trampoline over to new management UI
- 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
index f96c04ee..5d4e7a45 100644
--- a/ui/src/com/android/providers/downloads/ui/TrampolineActivity.java
+++ b/ui/src/com/android/providers/downloads/ui/TrampolineActivity.java
@@ -47,6 +47,7 @@ public class TrampolineActivity extends Activity {
private static final String KEY_ID = "id";
private static final String KEY_REASON = "reason";
+ private static final String KEY_SIZE = "size";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -59,12 +60,15 @@ public class TrampolineActivity extends Activity {
final int status;
final int reason;
+ final long size;
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));
+ size = cursor.getLong(
+ cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
} else {
Toast.makeText(this, R.string.dialog_file_missing_body, Toast.LENGTH_SHORT).show();
finish();
@@ -84,7 +88,7 @@ public class TrampolineActivity extends Activity {
case DownloadManager.STATUS_PAUSED:
if (reason == DownloadManager.PAUSED_QUEUED_FOR_WIFI) {
- PausedDialogFragment.show(getFragmentManager(), id);
+ PausedDialogFragment.show(getFragmentManager(), id, size);
} else {
sendRunningDownloadClickedBroadcast(id);
finish();
@@ -113,10 +117,11 @@ public class TrampolineActivity extends Activity {
}
public static class PausedDialogFragment extends DialogFragment {
- public static void show(FragmentManager fm, long id) {
+ public static void show(FragmentManager fm, long id, long size) {
final PausedDialogFragment dialog = new PausedDialogFragment();
final Bundle args = new Bundle();
args.putLong(KEY_ID, id);
+ args.putLong(KEY_SIZE, size);
dialog.setArguments(args);
dialog.show(fm, TAG_PAUSED);
}
@@ -130,13 +135,27 @@ public class TrampolineActivity extends Activity {
dm.setAccessAllDownloads(true);
final long id = getArguments().getLong(KEY_ID);
+ final long size = getArguments().getLong(KEY_SIZE);
final AlertDialog.Builder builder = new AlertDialog.Builder(
- context, AlertDialog.THEME_HOLO_LIGHT);
+ context, android.R.style.Theme_DeviceDefault_Light_Dialog_Alert);
builder.setTitle(R.string.dialog_title_queued_body);
builder.setMessage(R.string.dialog_queued_body);
- builder.setPositiveButton(R.string.keep_queued_download, null);
+ final Long maxSize = DownloadManager.getMaxBytesOverMobile(context);
+ if (maxSize != null && size > maxSize) {
+ // When we have a max size, we have no choice
+ builder.setPositiveButton(R.string.keep_queued_download, null);
+ } else {
+ // Give user the choice of starting now
+ builder.setPositiveButton(R.string.start_now_download,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dm.forceDownload(id);
+ }
+ });
+ }
builder.setNegativeButton(
R.string.remove_download, new DialogInterface.OnClickListener() {
@@ -152,7 +171,10 @@ public class TrampolineActivity extends Activity {
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
- getActivity().finish();
+ final Activity activity = getActivity();
+ if (activity != null) {
+ activity.finish();
+ }
}
}
@@ -178,10 +200,9 @@ public class TrampolineActivity extends Activity {
final int reason = getArguments().getInt(KEY_REASON);
final AlertDialog.Builder builder = new AlertDialog.Builder(
- context, AlertDialog.THEME_HOLO_LIGHT);
+ context, android.R.style.Theme_DeviceDefault_Light_Dialog_Alert);
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);
@@ -221,7 +242,10 @@ public class TrampolineActivity extends Activity {
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
- getActivity().finish();
+ final Activity activity = getActivity();
+ if (activity != null) {
+ activity.finish();
+ }
}
}
}