resolved conflicts for merge of d8eb423a to klp-dev
Change-Id: I44c7c7d0e1da8fd169a0bf57696e308631fa9f25
diff --git a/Android.mk b/Android.mk
index fb8cb6e..eb20588 100644
--- a/Android.mk
+++ b/Android.mk
@@ -10,5 +10,6 @@
LOCAL_PACKAGE_NAME := MediaProvider
LOCAL_CERTIFICATE := media
+LOCAL_PRIVILEGED_MODULE := true
include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 693d20c..7d48a4a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2,7 +2,7 @@
package="com.android.providers.media"
android:sharedUserId="android.media"
android:sharedUserLabel="@string/uid_label"
- android:versionCode="601">
+ android:versionCode="700">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
@@ -25,6 +25,17 @@
android:writePermission="android.permission.WRITE_EXTERNAL_STORAGE" />
</provider>
+ <provider
+ android:name="MediaDocumentsProvider"
+ android:authorities="com.android.providers.media.documents"
+ android:grantUriPermissions="true"
+ android:exported="true"
+ android:permission="android.permission.MANAGE_DOCUMENTS">
+ <intent-filter>
+ <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
+ </intent-filter>
+ </provider>
+
<!-- Handles database upgrades after OTAs, then disables itself -->
<receiver android:name="MediaUpgradeReceiver">
<!-- This broadcast is sent after the core system has finished
diff --git a/res/mipmap-hdpi/ic_launcher_gallery.png b/res/mipmap-hdpi/ic_launcher_gallery.png
new file mode 100644
index 0000000..23ea998
--- /dev/null
+++ b/res/mipmap-hdpi/ic_launcher_gallery.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_gallery.png b/res/mipmap-mdpi/ic_launcher_gallery.png
new file mode 100644
index 0000000..e1a9949
--- /dev/null
+++ b/res/mipmap-mdpi/ic_launcher_gallery.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_gallery.png b/res/mipmap-xhdpi/ic_launcher_gallery.png
new file mode 100644
index 0000000..79544a2
--- /dev/null
+++ b/res/mipmap-xhdpi/ic_launcher_gallery.png
Binary files differ
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 9deaea5..f37a5bb 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Mediaberging"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Gradeer mediadatabasis op."</string>
<string name="artist_label" msgid="1181678850424271227">"Kunstenaar"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Verstek luitoon"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Verstek kennisgewingklank"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Verstek wekkerklank"</string>
+ <string name="root_images" msgid="7098113056247445324">"Prente"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Video\'s"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Oudio"</string>
</resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index d9f30d9..561e6c5 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -16,8 +16,14 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="uid_label" msgid="999888504096344278">"ማህደረመረጃ"</string>
- <string name="app_label" msgid="581696352855930028">"ማህደረመረጃ ማከማቻ"</string>
+ <string name="uid_label" msgid="999888504096344278">" ማህደረ መረጃ"</string>
+ <string name="app_label" msgid="581696352855930028">" ማህደረ መረጃ ማከማቻ"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"የሚዲያ ውሂብ ጎታ በማሻሻል ላይ፡፡"</string>
<string name="artist_label" msgid="1181678850424271227">"አርቲስት"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"ነባሪ የደወል ቅላጼ"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"ነባሪ የማሳወቂያ ድምጽ"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"ነባሪ የማንቂያ ድምጽ"</string>
+ <string name="root_images" msgid="7098113056247445324">"ምስሎች"</string>
+ <string name="root_videos" msgid="3304457332406057833">"ቪዲዮዎች"</string>
+ <string name="root_audio" msgid="7177565505195715659">"ድምጽ"</string>
</resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index c990466..d396b58 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"تخزين الوسائط"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"جارٍ ترقية قاعدة بيانات الوسائط."</string>
<string name="artist_label" msgid="1181678850424271227">"الفنان"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"نغمة الرنين الافتراضية"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"صوت الإشعار الافتراضي"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"صوت التنبيه الافتراضي"</string>
+ <string name="root_images" msgid="7098113056247445324">"صور"</string>
+ <string name="root_videos" msgid="3304457332406057833">"مقاطع فيديو"</string>
+ <string name="root_audio" msgid="7177565505195715659">"الصوت"</string>
</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 1e12072..05908e8 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Медыянакапляльнік"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Абнаўленне базы дадзеных мультымедыя."</string>
<string name="artist_label" msgid="1181678850424271227">"Выканаўца"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Рынгтон па змаўчанні"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Сігнал паведамлення па змаўчаннi"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Сігнал будзільніка па змаўчанні"</string>
+ <string name="root_images" msgid="7098113056247445324">"Выявы"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Відэа"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Аўдыё"</string>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index d29ce4f..50ad9d6 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Хранилище на медии"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Базата от данни на носителите се надстройва."</string>
<string name="artist_label" msgid="1181678850424271227">"Изпълнител"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Стандартна мелодия"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Стандартен звук за известяване"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Стандартен звук за будилника"</string>
+ <string name="root_images" msgid="7098113056247445324">"Изображения"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Видеоклипове"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Аудио"</string>
</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 1cdc28e..aecc22b 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Emmagatzematge de mitjans"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"S\'està actualitzant la base de dades multimèdia."</string>
<string name="artist_label" msgid="1181678850424271227">"Intèrpret"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"To predeterminat"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"So de notificació predeterminat"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"So d\'alarma predeterminat"</string>
+ <string name="root_images" msgid="7098113056247445324">"Imatges"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Vídeos"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Àudio"</string>
</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index f87691c..d248cd8 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Úložiště médií"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Aktualizace databáze médií."</string>
<string name="artist_label" msgid="1181678850424271227">"Interpret"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Výchozí vyzváněcí tón"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Výchozí zvuk oznámení"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Výchozí zvuk budíku"</string>
+ <string name="root_images" msgid="7098113056247445324">"Obrázky"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videa"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Zvuk"</string>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index e6b57ce..439cd6c 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Medielagring"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Opgraderer mediedatabasen."</string>
<string name="artist_label" msgid="1181678850424271227">"Kunstner"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Standardringetone"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Standardlyd for underretninger"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Standardlyd for alarmer"</string>
+ <string name="root_images" msgid="7098113056247445324">"Billeder"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videoer"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Lyd"</string>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 8b9da85..d4a2a2f 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Medienspeicher"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Medien-Datenbank wird aktualisiert."</string>
<string name="artist_label" msgid="1181678850424271227">"Interpret"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Standard-Klingelton"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Standard-Benachrichtigungston"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Standard-Weckerton"</string>
+ <string name="root_images" msgid="7098113056247445324">"Bilder"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videos"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index da973fd..dc84490 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Χώρος αποθήκευσης μέσων"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Αναβάθμιση βάσης δεδομένων μέσων"</string>
<string name="artist_label" msgid="1181678850424271227">"Καλλιτέχνης"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Προεπιλεγμένος ήχος κλήσης"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Προεπιλεγμένος ήχος ειδοποίησης"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Προεπιλεγμένος ήχος ειδοποίησης"</string>
+ <string name="root_images" msgid="7098113056247445324">"Εικόνες"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Βίντεο"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Ήχος"</string>
</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 0b28a1e..6945912 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Media Storage"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Upgrading Media database."</string>
<string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Default ringtone"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Default notification sound"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Default alarm sound"</string>
+ <string name="root_images" msgid="7098113056247445324">"Images"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videos"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..6945912
--- /dev/null
+++ b/res/values-en-rIN/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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="uid_label" msgid="999888504096344278">"Media"</string>
+ <string name="app_label" msgid="581696352855930028">"Media Storage"</string>
+ <string name="upgrade_msg" msgid="4093462661265175619">"Upgrading Media database."</string>
+ <string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Default ringtone"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Default notification sound"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Default alarm sound"</string>
+ <string name="root_images" msgid="7098113056247445324">"Images"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videos"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
+</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index d2a38f6..7edb0eb 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Almacenamiento de medios"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Actualizando la base de datos de los medios"</string>
<string name="artist_label" msgid="1181678850424271227">"Artista"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Tono predeterminado"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Sonido de notificación predeterminado"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Sonido de alarma predeterminado"</string>
+ <string name="root_images" msgid="7098113056247445324">"Imágenes"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videos"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index e1382eb..c2aa127 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Almacenamiento de medios"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Actualizando base de datos multimedia"</string>
<string name="artist_label" msgid="1181678850424271227">"Artista"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Tono predeterminado"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Sonido de notificación predeterminado"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Sonido de alarma predeterminado"</string>
+ <string name="root_images" msgid="7098113056247445324">"Imágenes"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Vídeos"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
new file mode 100644
index 0000000..f36166a
--- /dev/null
+++ b/res/values-et-rEE/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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="uid_label" msgid="999888504096344278">"Meedium"</string>
+ <string name="app_label" msgid="581696352855930028">"Salvestusmeedium"</string>
+ <string name="upgrade_msg" msgid="4093462661265175619">"Meediumi andmebaasi uuendamine."</string>
+ <string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Vaikehelin"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Märguande vaikeheli"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Äratuse vaikeheli"</string>
+ <string name="root_images" msgid="7098113056247445324">"Pildid"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videod"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Heli"</string>
+</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 4b9dcdb..593a2c7 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -20,4 +20,9 @@
<string name="app_label" msgid="581696352855930028">"Salvestusmeedium"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Meediumi andmebaasi uuendamine."</string>
<string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Vaikehelin"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Märguande vaikeheli"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Äratuse vaikeheli"</string>
+ <string name="root_images" msgid="7098113056247445324">"Pildid"</string>
+ <string name="root_music" msgid="4663573634162145060">"Muusika"</string>
</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 1caae4a..56a0526 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"حافظه رسانه"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"ارتقا پایگاه داده رسانه."</string>
<string name="artist_label" msgid="1181678850424271227">"هنرمند"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"آهنگ زنگ پیشفرض"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"صدای اعلان پیشفرض"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"آهنگ زنگ پیشفرض"</string>
+ <string name="root_images" msgid="7098113056247445324">"تصاویر"</string>
+ <string name="root_videos" msgid="3304457332406057833">"ویدیوها"</string>
+ <string name="root_audio" msgid="7177565505195715659">"صدا"</string>
</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index bcdd937..6453c3f 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Median tallennustila"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Päivitetään Median tietokantaa."</string>
<string name="artist_label" msgid="1181678850424271227">"Esittäjä"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Oletussoittoääni"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Ilmoituksen oletusääni"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Hälytyksen oletusääni"</string>
+ <string name="root_images" msgid="7098113056247445324">"Kuvat"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videot"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Ääni"</string>
</resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..0da5059
--- /dev/null
+++ b/res/values-fr-rCA/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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="uid_label" msgid="999888504096344278">"Médias"</string>
+ <string name="app_label" msgid="581696352855930028">"Stockage multimédia"</string>
+ <string name="upgrade_msg" msgid="4093462661265175619">"Mise à jour de la base de données multimédia."</string>
+ <string name="artist_label" msgid="1181678850424271227">"Artiste"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Sonnerie par défaut"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Son de notification par défaut"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Son de l\'alarme par défaut"</string>
+ <string name="root_images" msgid="7098113056247445324">"Images"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Vidéos"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
+</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 65e3b86..c1f9c04 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Stockage multimédia"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Mise à jour de la base de données multimédia."</string>
<string name="artist_label" msgid="1181678850424271227">"Artiste"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Sonnerie par défaut"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Son de notification par défaut"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Son de l\'alarme par défaut"</string>
+ <string name="root_images" msgid="7098113056247445324">"Images"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Vidéos"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index a7736bb..ad4d9ad 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"मीडिया संग्रहण"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"मीडिया डेटाबेस अपग्रेड हो रहा है."</string>
<string name="artist_label" msgid="1181678850424271227">"कलाकार"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"डिफ़ॉल्ट रिंगटोन"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"सामान्य सूचना ध्वनी"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"डिफ़ॉल्ट अलार्म ध्वनि"</string>
+ <string name="root_images" msgid="7098113056247445324">"चित्र"</string>
+ <string name="root_videos" msgid="3304457332406057833">"वीडियो"</string>
+ <string name="root_audio" msgid="7177565505195715659">"ऑडियो"</string>
</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 45fe3f8..be7b286 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Prostor za pohranjivanje na mediju"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Nadogradnja baze podataka medija."</string>
<string name="artist_label" msgid="1181678850424271227">"Izvođač"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Zadana melodija zvona"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Zadani zvuk obavijesti"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Zadani zvuk alarma"</string>
+ <string name="root_images" msgid="7098113056247445324">"Slike"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videozapisi"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index b57a400..804cab5 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Médiatároló"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Médiaszolgáltató adatbázisának frissítése"</string>
<string name="artist_label" msgid="1181678850424271227">"Előadó"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Alapértelmezett csengőhang"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Alapértelmezett értesítési hang"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Alapértelmezett ébresztési hang"</string>
+ <string name="root_images" msgid="7098113056247445324">"Képek"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videók"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Hang"</string>
</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 7bda4a2..11a86e7 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Penyimpanan Media"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Meningkatkan versi basis data Media."</string>
<string name="artist_label" msgid="1181678850424271227">"Artis"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Nada dering default"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Suara pemberitahuan default"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Suara alarm default"</string>
+ <string name="root_images" msgid="7098113056247445324">"Gambar"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Video"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 848aa63..5d53d08 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Media Storage"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Upgrade del database di Media."</string>
<string name="artist_label" msgid="1181678850424271227">"Artista"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Suoneria predefinita"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Suono di notifica predefinito"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Suono allarme predefinito"</string>
+ <string name="root_images" msgid="7098113056247445324">"Immagini"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Video"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index f8502dc..0905702 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"אחסון מדיה"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"משדרג את מסד הנתונים של המדיה."</string>
<string name="artist_label" msgid="1181678850424271227">"אמן"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"רינגטון ברירת מחדל"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"צליל ברירת מחדל להתראה"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"צליל ברירת מחדל להתרעה"</string>
+ <string name="root_images" msgid="7098113056247445324">"תמונות"</string>
+ <string name="root_videos" msgid="3304457332406057833">"סרטונים"</string>
+ <string name="root_audio" msgid="7177565505195715659">"אודיו"</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 852df60..ac58c3f 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"メディアストレージ"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"メディアデータベースをアップグレードしています。"</string>
<string name="artist_label" msgid="1181678850424271227">"アーティスト"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"デフォルトの着信音"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"デフォルトの通知音"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"デフォルトの警告音"</string>
+ <string name="root_images" msgid="7098113056247445324">"画像"</string>
+ <string name="root_videos" msgid="3304457332406057833">"動画"</string>
+ <string name="root_audio" msgid="7177565505195715659">"音声"</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index a97c30a..ffb9664 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"미디어 저장소"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"미디어 데이터베이스 업그레이드 중"</string>
<string name="artist_label" msgid="1181678850424271227">"아티스트"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"기본 벨소리"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"기본 알림 소리"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"기본 알람 소리"</string>
+ <string name="root_images" msgid="7098113056247445324">"이미지"</string>
+ <string name="root_videos" msgid="3304457332406057833">"동영상"</string>
+ <string name="root_audio" msgid="7177565505195715659">"오디오"</string>
</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 41c67be..7b5be76 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Medijos saugykla"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Naujovinama medijos duomenų bazė."</string>
<string name="artist_label" msgid="1181678850424271227">"Atlikėjas"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Numatytasis skambėjimo tonas"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Numatytasis pranešimo garsas"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Numatytasis signalo garsas"</string>
+ <string name="root_images" msgid="7098113056247445324">"Vaizdai"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Vaizdo įrašai"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Garso įrašai"</string>
</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 5da36c7..93cd9e4 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Multivides krātuve"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Tiek jaunināta multivides datu bāze."</string>
<string name="artist_label" msgid="1181678850424271227">"Izpildītājs"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Noklusējuma zvana signāls"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Paziņojuma noklusējuma skaņa"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Signāla noklusējuma skaņa"</string>
+ <string name="root_images" msgid="7098113056247445324">"Attēli"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videoklipi"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
new file mode 100644
index 0000000..cef695d
--- /dev/null
+++ b/res/values-ms-rMY/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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="uid_label" msgid="999888504096344278">"Media"</string>
+ <string name="app_label" msgid="581696352855930028">"Storan Media"</string>
+ <string name="upgrade_msg" msgid="4093462661265175619">"Menaiktaraf pangkalan data Media."</string>
+ <string name="artist_label" msgid="1181678850424271227">"Artis"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Nada dering lalai"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Bunyi pemberitahuan lalai"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Bunyi penggera lalai"</string>
+ <string name="root_images" msgid="7098113056247445324">"Imej"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Video"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
+</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index ad1b139..7f2ae5b 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -20,4 +20,9 @@
<string name="app_label" msgid="581696352855930028">"Storan Media"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Menaiktaraf pangkalan data Media."</string>
<string name="artist_label" msgid="1181678850424271227">"Artis"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Nada dering lalai"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Bunyi pemberitahuan lalai"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Bunyi penggera lalai"</string>
+ <string name="root_images" msgid="7098113056247445324">"Imej"</string>
+ <string name="root_music" msgid="4663573634162145060">"Muzik"</string>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 9e6b81c..e566c21 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Medielagring"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Oppgraderer mediedatabase."</string>
<string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Standard ringetone"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Standard varsellyd"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Standard alarmlyd"</string>
+ <string name="root_images" msgid="7098113056247445324">"Bilder"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videoer"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Lyd"</string>
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index b31a15d..f2e1fde 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Mediaopslag"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Mediadatabase bijwerken."</string>
<string name="artist_label" msgid="1181678850424271227">"Artiest"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Standaardbeltoon"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Standaardmeldingsgeluid"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Standaardalarmgeluid"</string>
+ <string name="root_images" msgid="7098113056247445324">"Afbeeldingen"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Video\'s"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 420c2e0..43b9041 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Przechowywanie multimediów"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Uaktualnianie bazy danych mediów."</string>
<string name="artist_label" msgid="1181678850424271227">"Wykonawca"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Dzwonek domyślny"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Domyślny dźwięk powiadomienia"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Domyślny dźwięk alarmu"</string>
+ <string name="root_images" msgid="7098113056247445324">"Obrazy"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Filmy"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Dźwięk"</string>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 2aa595d..daea25a 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Armazenamento de multimédia"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"A atualizar a base de dados de Multimédia."</string>
<string name="artist_label" msgid="1181678850424271227">"Artista"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Toque predefinido"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Som de notificação predefinido"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Som de alarme predefinido"</string>
+ <string name="root_images" msgid="7098113056247445324">"Imagens"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Vídeos"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Áudio"</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index eb464c9..9362c9a 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Armazenamento de mídia"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Atualizando o banco de dados de mídia."</string>
<string name="artist_label" msgid="1181678850424271227">"Artista"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Toque padrão"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Som de notificação padrão"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Som de alarme padrão"</string>
+ <string name="root_images" msgid="7098113056247445324">"Imagens"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Vídeos"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Áudio"</string>
</resources>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index e1298b4..75fec6d 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -21,4 +21,16 @@
<!-- no translation found for upgrade_msg (4093462661265175619) -->
<skip />
<string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+ <!-- no translation found for ringtone_default (3332469542084549546) -->
+ <skip />
+ <!-- no translation found for notification_sound_default (5309766396223094324) -->
+ <skip />
+ <!-- no translation found for alarm_sound_default (6854264112790616066) -->
+ <skip />
+ <!-- no translation found for root_images (7098113056247445324) -->
+ <skip />
+ <!-- no translation found for root_videos (3304457332406057833) -->
+ <skip />
+ <!-- no translation found for root_audio (7177565505195715659) -->
+ <skip />
</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 2fecf5c..4f724f3 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Mediu de stocare"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Se actualizează baza de date media."</string>
<string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Ton de sonerie prestabilit"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Sunet de notificare prestabilit"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Sunet de alarmă prestabilit"</string>
+ <string name="root_images" msgid="7098113056247445324">"Imagini"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videoclipuri"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index ce89639..ea61c07 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Хранилище мультимедиа"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Обновление базы данных мультимедиа..."</string>
<string name="artist_label" msgid="1181678850424271227">"Исполнитель"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Рингтон по умолчанию"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Звук уведомлений по умолчанию"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Звуковой сигнал по умолчанию"</string>
+ <string name="root_images" msgid="7098113056247445324">"Изображения"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Видео"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Аудио"</string>
</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 9b3f90d..2dcc7e2 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Ukladací priestor médií"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Prebieha inovácia databázy médií"</string>
<string name="artist_label" msgid="1181678850424271227">"Umelec"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Predvolený tón zvonenia"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Predvolený zvuk upozornenia"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Predvolený zvuk budíka"</string>
+ <string name="root_images" msgid="7098113056247445324">"Obrázky"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videá"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Zvuk"</string>
</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index fef7ee7..61a525f 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Prostor za shranjevanje predstavnosti"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Nadgradnja večpredstavnostne zbirke podatkov."</string>
<string name="artist_label" msgid="1181678850424271227">"Izvajalec"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Privzeti ton zvonjenja"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Privzeti zvok obvestila"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Privzeti zvok alarma"</string>
+ <string name="root_images" msgid="7098113056247445324">"Slike"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videoposnetki"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Zvok"</string>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index bacfcb1..1a1024c 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Складиште за медије"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Надограђивање базе података медија."</string>
<string name="artist_label" msgid="1181678850424271227">"Извођач"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Подразумевана мелодија звона"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Подразумевани звук обавештења"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Подразумевани звук аларма"</string>
+ <string name="root_images" msgid="7098113056247445324">"Слике"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Видео снимци"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Звук"</string>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 7930730..fb0af08 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Medialagring"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Uppgraderar mediedatabas."</string>
<string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Standardringsignal"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Standardljud för meddelanden"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Standardljud för alarm"</string>
+ <string name="root_images" msgid="7098113056247445324">"Bilder"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videor"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Ljud"</string>
</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 7095b78..9374a8b 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -16,8 +16,14 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="uid_label" msgid="999888504096344278">"Midia"</string>
+ <string name="uid_label" msgid="999888504096344278">"Media"</string>
<string name="app_label" msgid="581696352855930028">"Hifadhi ya media"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Inapandisha daraja la hifadhidata ya Midia."</string>
<string name="artist_label" msgid="1181678850424271227">"Msanii"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Mlio wa simu chaguo-msingi"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Sauti chaguo-msingi ya arifa"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Sauti chaguo-msingi ya kengele"</string>
+ <string name="root_images" msgid="7098113056247445324">"Picha"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Video"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Sauti"</string>
</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index b962aa1..a227e3e 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"พื้นที่เก็บข้อมูลสื่อ"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"กำลังอัปเกรดฐานข้อมูลสื่อ"</string>
<string name="artist_label" msgid="1181678850424271227">"ศิลปิน"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"เสียงเรียกเข้าเริ่มต้น"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"เสียงแจ้งเตือนเริ่มต้น"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"เสียงปลุกเริ่มต้น"</string>
+ <string name="root_images" msgid="7098113056247445324">"รูปภาพ"</string>
+ <string name="root_videos" msgid="3304457332406057833">"วิดีโอ"</string>
+ <string name="root_audio" msgid="7177565505195715659">"เสียง"</string>
</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 026a8ad..4d381bd 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Imbakan ng Media"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Ina-upgrade ang database ng Media."</string>
<string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Default na ringtone"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Default na tunog ng notification"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Default na tunog ng alarma"</string>
+ <string name="root_images" msgid="7098113056247445324">"Mga Larawan"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Mga Video"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 196822d..7f62bf2 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Medya Deposu"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Medya veritabanı yeni sürüme geçiriliyor."</string>
<string name="artist_label" msgid="1181678850424271227">"Sanatçı"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Varsayılan zil sesi"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Varsayılan bildirim sesi"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Varsayılan alarm sesi"</string>
+ <string name="root_images" msgid="7098113056247445324">"Resimler"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Videolar"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Ses"</string>
</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index ce61d15..bb458d8 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Збер. медіа-файл."</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Оновлення бази даних медіа-файлів."</string>
<string name="artist_label" msgid="1181678850424271227">"Виконавець"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Сигнал дзвінка за умовчанням"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Звук сповіщення за умовчанням"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Звук сигналу за умовчанням"</string>
+ <string name="root_images" msgid="7098113056247445324">"Зображення"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Відео"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Звук"</string>
</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index a501f3e..79fdf74 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Bộ nhớ Phương tiện"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Đang nâng cấp cơ sở dữ liệu Phương tiện."</string>
<string name="artist_label" msgid="1181678850424271227">"Nghệ sĩ"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Nhạc chuông mặc định"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Âm thanh thông báo mặc định"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Âm thanh báo thức mặc định"</string>
+ <string name="root_images" msgid="7098113056247445324">"Hình ảnh"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Video"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Âm thanh"</string>
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index d29ddad..9bf46c9 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -19,5 +19,11 @@
<string name="uid_label" msgid="999888504096344278">"媒体"</string>
<string name="app_label" msgid="581696352855930028">"媒体存储"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"正在升级媒体数据库。"</string>
- <string name="artist_label" msgid="1181678850424271227">"艺术家"</string>
+ <string name="artist_label" msgid="1181678850424271227">"音乐人"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"默认铃声"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"默认通知提示音"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"默认闹钟提示音"</string>
+ <string name="root_images" msgid="7098113056247445324">"图片"</string>
+ <string name="root_videos" msgid="3304457332406057833">"视频"</string>
+ <string name="root_audio" msgid="7177565505195715659">"音频"</string>
</resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..6da7650
--- /dev/null
+++ b/res/values-zh-rHK/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 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="uid_label" msgid="999888504096344278">"媒體"</string>
+ <string name="app_label" msgid="581696352855930028">"媒體儲存空間"</string>
+ <string name="upgrade_msg" msgid="4093462661265175619">"正在升級媒體資料庫。"</string>
+ <string name="artist_label" msgid="1181678850424271227">"歌手/創作人"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"預設鈴聲"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"預設通知音效"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"預設鬧鐘音效"</string>
+ <string name="root_images" msgid="7098113056247445324">"圖片"</string>
+ <string name="root_videos" msgid="3304457332406057833">"影片"</string>
+ <string name="root_audio" msgid="7177565505195715659">"音頻"</string>
+</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 8a88124..bbc4769 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"媒體儲存空間"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"正在升級媒體資料庫。"</string>
<string name="artist_label" msgid="1181678850424271227">"演出者"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"預設鈴聲"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"預設通知音效"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"預設鬧鐘音效"</string>
+ <string name="root_images" msgid="7098113056247445324">"圖片"</string>
+ <string name="root_videos" msgid="3304457332406057833">"影片"</string>
+ <string name="root_audio" msgid="7177565505195715659">"音訊"</string>
</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 8a68cf7..e76085c 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -20,4 +20,10 @@
<string name="app_label" msgid="581696352855930028">"Isitoreji Semidiya"</string>
<string name="upgrade_msg" msgid="4093462661265175619">"Ukufaka ezakamuva kwimininingo egciniwe yeMediya."</string>
<string name="artist_label" msgid="1181678850424271227">"Umculi"</string>
+ <string name="ringtone_default" msgid="3332469542084549546">"Ithoni yokukhala ezenzakalelayo"</string>
+ <string name="notification_sound_default" msgid="5309766396223094324">"Umsindo wesaziso ozenzakalelayo"</string>
+ <string name="alarm_sound_default" msgid="6854264112790616066">"Umsindo we-alamu ozenzakalelayo"</string>
+ <string name="root_images" msgid="7098113056247445324">"Izithombe"</string>
+ <string name="root_videos" msgid="3304457332406057833">"Amavidiyo"</string>
+ <string name="root_audio" msgid="7177565505195715659">"Umsindo"</string>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1c6f3ab..8e8c541 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -36,4 +36,12 @@
<!-- Choice in the alarm sound picker. If chosen, the default alarm sound will be used. -->
<string name="alarm_sound_default">Default alarm sound</string>
+
+ <!-- Title for documents backend that offers images. [CHAR LIMIT=24] -->
+ <string name="root_images">Images</string>
+ <!-- Title for documents backend that offers videos. [CHAR LIMIT=24] -->
+ <string name="root_videos">Videos</string>
+ <!-- Title for documents backend that offers audio. [CHAR LIMIT=24] -->
+ <string name="root_audio">Audio</string>
+
</resources>
diff --git a/src/com/android/providers/media/MediaDocumentsProvider.java b/src/com/android/providers/media/MediaDocumentsProvider.java
new file mode 100644
index 0000000..a887a83
--- /dev/null
+++ b/src/com/android/providers/media/MediaDocumentsProvider.java
@@ -0,0 +1,923 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.provider.BaseColumns;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsProvider;
+import android.provider.MediaStore.Audio;
+import android.provider.MediaStore.Audio.AlbumColumns;
+import android.provider.MediaStore.Audio.Albums;
+import android.provider.MediaStore.Audio.ArtistColumns;
+import android.provider.MediaStore.Audio.Artists;
+import android.provider.MediaStore.Audio.AudioColumns;
+import android.provider.MediaStore.Files.FileColumns;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.Images.ImageColumns;
+import android.provider.MediaStore.Video;
+import android.provider.MediaStore.Video.VideoColumns;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * Presents a {@link DocumentsContract} view of {@link MediaProvider} external
+ * contents.
+ */
+public class MediaDocumentsProvider extends DocumentsProvider {
+ private static final String TAG = "MediaDocumentsProvider";
+
+ private static final String AUTHORITY = "com.android.providers.media.documents";
+
+ private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
+ Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
+ Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_MIME_TYPES
+ };
+
+ private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
+ Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
+ Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
+ };
+
+ private static final String IMAGE_MIME_TYPES = joinNewline("image/*");
+
+ private static final String VIDEO_MIME_TYPES = joinNewline("video/*");
+
+ private static final String AUDIO_MIME_TYPES = joinNewline(
+ "audio/*", "application/ogg", "application/x-flac");
+
+ private static final String TYPE_IMAGES_ROOT = "images_root";
+ private static final String TYPE_IMAGES_BUCKET = "images_bucket";
+ private static final String TYPE_IMAGE = "image";
+
+ private static final String TYPE_VIDEOS_ROOT = "videos_root";
+ private static final String TYPE_VIDEOS_BUCKET = "videos_bucket";
+ private static final String TYPE_VIDEO = "video";
+
+ private static final String TYPE_AUDIO_ROOT = "audio_root";
+ private static final String TYPE_AUDIO = "audio";
+ private static final String TYPE_ARTIST = "artist";
+ private static final String TYPE_ALBUM = "album";
+
+ private static boolean sReturnedImagesEmpty = false;
+ private static boolean sReturnedVideosEmpty = false;
+ private static boolean sReturnedAudioEmpty = false;
+
+ private static String joinNewline(String... args) {
+ return TextUtils.join("\n", args);
+ }
+
+ private void copyNotificationUri(MatrixCursor result, Cursor cursor) {
+ result.setNotificationUri(getContext().getContentResolver(), cursor.getNotificationUri());
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ private static void notifyRootsChanged(Context context) {
+ context.getContentResolver()
+ .notifyChange(DocumentsContract.buildRootsUri(AUTHORITY), null, false);
+ }
+
+ /**
+ * When inserting the first item of each type, we need to trigger a roots
+ * refresh to clear a previously reported {@link Root#FLAG_EMPTY}.
+ */
+ static void onMediaStoreInsert(Context context, String volumeName, int type, long id) {
+ if (!"external".equals(volumeName)) return;
+
+ if (type == FileColumns.MEDIA_TYPE_IMAGE && sReturnedImagesEmpty) {
+ sReturnedImagesEmpty = false;
+ notifyRootsChanged(context);
+ } else if (type == FileColumns.MEDIA_TYPE_VIDEO && sReturnedVideosEmpty) {
+ sReturnedVideosEmpty = false;
+ notifyRootsChanged(context);
+ } else if (type == FileColumns.MEDIA_TYPE_AUDIO && sReturnedAudioEmpty) {
+ sReturnedAudioEmpty = false;
+ notifyRootsChanged(context);
+ }
+ }
+
+ /**
+ * When deleting an item, we need to revoke any outstanding Uri grants.
+ */
+ static void onMediaStoreDelete(Context context, String volumeName, int type, long id) {
+ if (!"external".equals(volumeName)) return;
+
+ if (type == FileColumns.MEDIA_TYPE_IMAGE) {
+ final Uri uri = DocumentsContract.buildDocumentUri(
+ AUTHORITY, getDocIdForIdent(TYPE_IMAGE, id));
+ context.revokeUriPermission(uri, ~0);
+ } else if (type == FileColumns.MEDIA_TYPE_VIDEO) {
+ final Uri uri = DocumentsContract.buildDocumentUri(
+ AUTHORITY, getDocIdForIdent(TYPE_VIDEO, id));
+ context.revokeUriPermission(uri, ~0);
+ } else if (type == FileColumns.MEDIA_TYPE_AUDIO) {
+ final Uri uri = DocumentsContract.buildDocumentUri(
+ AUTHORITY, getDocIdForIdent(TYPE_AUDIO, id));
+ context.revokeUriPermission(uri, ~0);
+ }
+ }
+
+ private static class Ident {
+ public String type;
+ public long id;
+ }
+
+ private static Ident getIdentForDocId(String docId) {
+ final Ident ident = new Ident();
+ final int split = docId.indexOf(':');
+ if (split == -1) {
+ ident.type = docId;
+ ident.id = -1;
+ } else {
+ ident.type = docId.substring(0, split);
+ ident.id = Long.parseLong(docId.substring(split + 1));
+ }
+ return ident;
+ }
+
+ private static String getDocIdForIdent(String type, long id) {
+ return type + ":" + id;
+ }
+
+ private static String[] resolveRootProjection(String[] projection) {
+ return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
+ }
+
+ private static String[] resolveDocumentProjection(String[] projection) {
+ return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
+ }
+
+ @Override
+ public Cursor queryRoots(String[] projection) throws FileNotFoundException {
+ final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
+ includeImagesRoot(result);
+ includeVideosRoot(result);
+ includeAudioRoot(result);
+ return result;
+ }
+
+ @Override
+ public Cursor queryDocument(String docId, String[] projection) throws FileNotFoundException {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+ final Ident ident = getIdentForDocId(docId);
+
+ final long token = Binder.clearCallingIdentity();
+ Cursor cursor = null;
+ try {
+ if (TYPE_IMAGES_ROOT.equals(ident.type)) {
+ // single root
+ includeImagesRootDocument(result);
+ } else if (TYPE_IMAGES_BUCKET.equals(ident.type)) {
+ // single bucket
+ cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+ ImagesBucketQuery.PROJECTION, ImageColumns.BUCKET_ID + "=" + ident.id,
+ null, ImagesBucketQuery.SORT_ORDER);
+ copyNotificationUri(result, cursor);
+ if (cursor.moveToFirst()) {
+ includeImagesBucket(result, cursor);
+ }
+ } else if (TYPE_IMAGE.equals(ident.type)) {
+ // single image
+ cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+ ImageQuery.PROJECTION, BaseColumns._ID + "=" + ident.id, null,
+ null);
+ copyNotificationUri(result, cursor);
+ if (cursor.moveToFirst()) {
+ includeImage(result, cursor);
+ }
+ } else if (TYPE_VIDEOS_ROOT.equals(ident.type)) {
+ // single root
+ includeVideosRootDocument(result);
+ } else if (TYPE_VIDEOS_BUCKET.equals(ident.type)) {
+ // single bucket
+ cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+ VideosBucketQuery.PROJECTION, VideoColumns.BUCKET_ID + "=" + ident.id,
+ null, VideosBucketQuery.SORT_ORDER);
+ copyNotificationUri(result, cursor);
+ if (cursor.moveToFirst()) {
+ includeVideosBucket(result, cursor);
+ }
+ } else if (TYPE_VIDEO.equals(ident.type)) {
+ // single video
+ cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+ VideoQuery.PROJECTION, BaseColumns._ID + "=" + ident.id, null,
+ null);
+ copyNotificationUri(result, cursor);
+ if (cursor.moveToFirst()) {
+ includeVideo(result, cursor);
+ }
+ } else if (TYPE_AUDIO_ROOT.equals(ident.type)) {
+ // single root
+ includeAudioRootDocument(result);
+ } else if (TYPE_ARTIST.equals(ident.type)) {
+ // single artist
+ cursor = resolver.query(Artists.EXTERNAL_CONTENT_URI,
+ ArtistQuery.PROJECTION, BaseColumns._ID + "=" + ident.id, null,
+ null);
+ copyNotificationUri(result, cursor);
+ if (cursor.moveToFirst()) {
+ includeArtist(result, cursor);
+ }
+ } else if (TYPE_ALBUM.equals(ident.type)) {
+ // single album
+ cursor = resolver.query(Albums.EXTERNAL_CONTENT_URI,
+ AlbumQuery.PROJECTION, BaseColumns._ID + "=" + ident.id, null,
+ null);
+ copyNotificationUri(result, cursor);
+ if (cursor.moveToFirst()) {
+ includeAlbum(result, cursor);
+ }
+ } else if (TYPE_AUDIO.equals(ident.type)) {
+ // single song
+ cursor = resolver.query(Audio.Media.EXTERNAL_CONTENT_URI,
+ SongQuery.PROJECTION, BaseColumns._ID + "=" + ident.id, null,
+ null);
+ copyNotificationUri(result, cursor);
+ if (cursor.moveToFirst()) {
+ includeAudio(result, cursor);
+ }
+ } else {
+ throw new UnsupportedOperationException("Unsupported document " + docId);
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ Binder.restoreCallingIdentity(token);
+ }
+ return result;
+ }
+
+ @Override
+ public Cursor queryChildDocuments(String docId, String[] projection, String sortOrder)
+ throws FileNotFoundException {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+ final Ident ident = getIdentForDocId(docId);
+
+ final long token = Binder.clearCallingIdentity();
+ Cursor cursor = null;
+ try {
+ if (TYPE_IMAGES_ROOT.equals(ident.type)) {
+ // include all unique buckets
+ cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+ ImagesBucketQuery.PROJECTION, null, null, ImagesBucketQuery.SORT_ORDER);
+ // multiple orders
+ copyNotificationUri(result, cursor);
+ long lastId = Long.MIN_VALUE;
+ while (cursor.moveToNext()) {
+ final long id = cursor.getLong(ImagesBucketQuery.BUCKET_ID);
+ if (lastId != id) {
+ includeImagesBucket(result, cursor);
+ lastId = id;
+ }
+ }
+ } else if (TYPE_IMAGES_BUCKET.equals(ident.type)) {
+ // include images under bucket
+ cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+ ImageQuery.PROJECTION, ImageColumns.BUCKET_ID + "=" + ident.id,
+ null, null);
+ copyNotificationUri(result, cursor);
+ while (cursor.moveToNext()) {
+ includeImage(result, cursor);
+ }
+ } else if (TYPE_VIDEOS_ROOT.equals(ident.type)) {
+ // include all unique buckets
+ cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+ VideosBucketQuery.PROJECTION, null, null, VideosBucketQuery.SORT_ORDER);
+ copyNotificationUri(result, cursor);
+ long lastId = Long.MIN_VALUE;
+ while (cursor.moveToNext()) {
+ final long id = cursor.getLong(VideosBucketQuery.BUCKET_ID);
+ if (lastId != id) {
+ includeVideosBucket(result, cursor);
+ lastId = id;
+ }
+ }
+ } else if (TYPE_VIDEOS_BUCKET.equals(ident.type)) {
+ // include videos under bucket
+ cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+ VideoQuery.PROJECTION, VideoColumns.BUCKET_ID + "=" + ident.id,
+ null, null);
+ copyNotificationUri(result, cursor);
+ while (cursor.moveToNext()) {
+ includeVideo(result, cursor);
+ }
+ } else if (TYPE_AUDIO_ROOT.equals(ident.type)) {
+ // include all artists
+ cursor = resolver.query(Audio.Artists.EXTERNAL_CONTENT_URI,
+ ArtistQuery.PROJECTION, null, null, null);
+ copyNotificationUri(result, cursor);
+ while (cursor.moveToNext()) {
+ includeArtist(result, cursor);
+ }
+ } else if (TYPE_ARTIST.equals(ident.type)) {
+ // include all albums under artist
+ cursor = resolver.query(Artists.Albums.getContentUri("external", ident.id),
+ AlbumQuery.PROJECTION, null, null, null);
+ copyNotificationUri(result, cursor);
+ while (cursor.moveToNext()) {
+ includeAlbum(result, cursor);
+ }
+ } else if (TYPE_ALBUM.equals(ident.type)) {
+ // include all songs under album
+ cursor = resolver.query(Audio.Media.EXTERNAL_CONTENT_URI,
+ SongQuery.PROJECTION, AudioColumns.ALBUM_ID + "=" + ident.id,
+ null, null);
+ copyNotificationUri(result, cursor);
+ while (cursor.moveToNext()) {
+ includeAudio(result, cursor);
+ }
+ } else {
+ throw new UnsupportedOperationException("Unsupported document " + docId);
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ Binder.restoreCallingIdentity(token);
+ }
+ return result;
+ }
+
+ @Override
+ public Cursor queryRecentDocuments(String rootId, String[] projection)
+ throws FileNotFoundException {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+
+ final long token = Binder.clearCallingIdentity();
+ Cursor cursor = null;
+ try {
+ if (TYPE_IMAGES_ROOT.equals(rootId)) {
+ // include all unique buckets
+ cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+ ImageQuery.PROJECTION, null, null, ImageColumns.DATE_MODIFIED + " DESC");
+ copyNotificationUri(result, cursor);
+ while (cursor.moveToNext() && result.getCount() < 64) {
+ includeImage(result, cursor);
+ }
+ } else if (TYPE_VIDEOS_ROOT.equals(rootId)) {
+ // include all unique buckets
+ cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+ VideoQuery.PROJECTION, null, null, VideoColumns.DATE_MODIFIED + " DESC");
+ copyNotificationUri(result, cursor);
+ while (cursor.moveToNext() && result.getCount() < 64) {
+ includeVideo(result, cursor);
+ }
+ } else {
+ throw new UnsupportedOperationException("Unsupported root " + rootId);
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ Binder.restoreCallingIdentity(token);
+ }
+ return result;
+ }
+
+ @Override
+ public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
+ throws FileNotFoundException {
+ final Ident ident = getIdentForDocId(docId);
+
+ if (!"r".equals(mode)) {
+ throw new IllegalArgumentException("Media is read-only");
+ }
+
+ final Uri target;
+ if (TYPE_IMAGE.equals(ident.type) && ident.id != -1) {
+ target = ContentUris.withAppendedId(
+ Images.Media.EXTERNAL_CONTENT_URI, ident.id);
+ } else if (TYPE_VIDEO.equals(ident.type) && ident.id != -1) {
+ target = ContentUris.withAppendedId(
+ Video.Media.EXTERNAL_CONTENT_URI, ident.id);
+ } else if (TYPE_AUDIO.equals(ident.type) && ident.id != -1) {
+ target = ContentUris.withAppendedId(
+ Audio.Media.EXTERNAL_CONTENT_URI, ident.id);
+ } else {
+ throw new UnsupportedOperationException("Unsupported document " + docId);
+ }
+
+ // Delegate to real provider
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getContext().getContentResolver().openFileDescriptor(target, mode);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public AssetFileDescriptor openDocumentThumbnail(
+ String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Ident ident = getIdentForDocId(docId);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (TYPE_IMAGES_BUCKET.equals(ident.type)) {
+ final long id = getImageForBucketCleared(ident.id);
+ return openOrCreateImageThumbnailCleared(id, signal);
+ } else if (TYPE_IMAGE.equals(ident.type)) {
+ return openOrCreateImageThumbnailCleared(ident.id, signal);
+ } else if (TYPE_VIDEOS_BUCKET.equals(ident.type)) {
+ final long id = getVideoForBucketCleared(ident.id);
+ return openOrCreateVideoThumbnailCleared(id, signal);
+ } else if (TYPE_VIDEO.equals(ident.type)) {
+ return openOrCreateVideoThumbnailCleared(ident.id, signal);
+ } else {
+ throw new UnsupportedOperationException("Unsupported document " + docId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private boolean isEmpty(Uri uri) {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final long token = Binder.clearCallingIdentity();
+ Cursor cursor = null;
+ try {
+ cursor = resolver.query(uri, new String[] {
+ BaseColumns._ID }, null, null, null);
+ return (cursor == null) || (cursor.getCount() == 0);
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void includeImagesRoot(MatrixCursor result) {
+ int flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_RECENTS;
+ if (isEmpty(Images.Media.EXTERNAL_CONTENT_URI)) {
+ flags |= Root.FLAG_EMPTY;
+ sReturnedImagesEmpty = true;
+ }
+
+ final RowBuilder row = result.newRow();
+ row.add(Root.COLUMN_ROOT_ID, TYPE_IMAGES_ROOT);
+ row.add(Root.COLUMN_FLAGS, flags);
+ row.add(Root.COLUMN_TITLE, getContext().getString(R.string.root_images));
+ row.add(Root.COLUMN_DOCUMENT_ID, TYPE_IMAGES_ROOT);
+ row.add(Root.COLUMN_MIME_TYPES, IMAGE_MIME_TYPES);
+ }
+
+ private void includeVideosRoot(MatrixCursor result) {
+ int flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_RECENTS;
+ if (isEmpty(Video.Media.EXTERNAL_CONTENT_URI)) {
+ flags |= Root.FLAG_EMPTY;
+ sReturnedVideosEmpty = true;
+ }
+
+ final RowBuilder row = result.newRow();
+ row.add(Root.COLUMN_ROOT_ID, TYPE_VIDEOS_ROOT);
+ row.add(Root.COLUMN_FLAGS, flags);
+ row.add(Root.COLUMN_TITLE, getContext().getString(R.string.root_videos));
+ row.add(Root.COLUMN_DOCUMENT_ID, TYPE_VIDEOS_ROOT);
+ row.add(Root.COLUMN_MIME_TYPES, VIDEO_MIME_TYPES);
+ }
+
+ private void includeAudioRoot(MatrixCursor result) {
+ int flags = Root.FLAG_LOCAL_ONLY;
+ if (isEmpty(Audio.Media.EXTERNAL_CONTENT_URI)) {
+ flags |= Root.FLAG_EMPTY;
+ sReturnedAudioEmpty = true;
+ }
+
+ final RowBuilder row = result.newRow();
+ row.add(Root.COLUMN_ROOT_ID, TYPE_AUDIO_ROOT);
+ row.add(Root.COLUMN_FLAGS, flags);
+ row.add(Root.COLUMN_TITLE, getContext().getString(R.string.root_audio));
+ row.add(Root.COLUMN_DOCUMENT_ID, TYPE_AUDIO_ROOT);
+ row.add(Root.COLUMN_MIME_TYPES, AUDIO_MIME_TYPES);
+ }
+
+ private void includeImagesRootDocument(MatrixCursor result) {
+ final RowBuilder row = result.newRow();
+ row.add(Document.COLUMN_DOCUMENT_ID, TYPE_IMAGES_ROOT);
+ row.add(Document.COLUMN_DISPLAY_NAME, getContext().getString(R.string.root_images));
+ row.add(Document.COLUMN_FLAGS,
+ Document.FLAG_DIR_PREFERS_GRID | Document.FLAG_DIR_PREFERS_LAST_MODIFIED);
+ row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ }
+
+ private void includeVideosRootDocument(MatrixCursor result) {
+ final RowBuilder row = result.newRow();
+ row.add(Document.COLUMN_DOCUMENT_ID, TYPE_VIDEOS_ROOT);
+ row.add(Document.COLUMN_DISPLAY_NAME, getContext().getString(R.string.root_videos));
+ row.add(Document.COLUMN_FLAGS,
+ Document.FLAG_DIR_PREFERS_GRID | Document.FLAG_DIR_PREFERS_LAST_MODIFIED);
+ row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ }
+
+ private void includeAudioRootDocument(MatrixCursor result) {
+ final RowBuilder row = result.newRow();
+ row.add(Document.COLUMN_DOCUMENT_ID, TYPE_AUDIO_ROOT);
+ row.add(Document.COLUMN_DISPLAY_NAME, getContext().getString(R.string.root_audio));
+ row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ }
+
+ private interface ImagesBucketQuery {
+ final String[] PROJECTION = new String[] {
+ ImageColumns.BUCKET_ID,
+ ImageColumns.BUCKET_DISPLAY_NAME,
+ ImageColumns.DATE_MODIFIED };
+ final String SORT_ORDER = ImageColumns.BUCKET_ID + ", " + ImageColumns.DATE_MODIFIED
+ + " DESC";
+
+ final int BUCKET_ID = 0;
+ final int BUCKET_DISPLAY_NAME = 1;
+ final int DATE_MODIFIED = 2;
+ }
+
+ private void includeImagesBucket(MatrixCursor result, Cursor cursor) {
+ final long id = cursor.getLong(ImagesBucketQuery.BUCKET_ID);
+ final String docId = getDocIdForIdent(TYPE_IMAGES_BUCKET, id);
+
+ final RowBuilder row = result.newRow();
+ row.add(Document.COLUMN_DOCUMENT_ID, docId);
+ row.add(Document.COLUMN_DISPLAY_NAME,
+ cursor.getString(ImagesBucketQuery.BUCKET_DISPLAY_NAME));
+ row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ row.add(Document.COLUMN_LAST_MODIFIED,
+ cursor.getLong(ImagesBucketQuery.DATE_MODIFIED) * DateUtils.SECOND_IN_MILLIS);
+ row.add(Document.COLUMN_FLAGS, Document.FLAG_DIR_PREFERS_GRID
+ | Document.FLAG_SUPPORTS_THUMBNAIL | Document.FLAG_DIR_PREFERS_LAST_MODIFIED
+ | Document.FLAG_DIR_HIDE_GRID_TITLES);
+ }
+
+ private interface ImageQuery {
+ final String[] PROJECTION = new String[] {
+ ImageColumns._ID,
+ ImageColumns.DISPLAY_NAME,
+ ImageColumns.MIME_TYPE,
+ ImageColumns.SIZE,
+ ImageColumns.DATE_MODIFIED };
+
+ final int _ID = 0;
+ final int DISPLAY_NAME = 1;
+ final int MIME_TYPE = 2;
+ final int SIZE = 3;
+ final int DATE_MODIFIED = 4;
+ }
+
+ private void includeImage(MatrixCursor result, Cursor cursor) {
+ final long id = cursor.getLong(ImageQuery._ID);
+ final String docId = getDocIdForIdent(TYPE_IMAGE, id);
+
+ final RowBuilder row = result.newRow();
+ row.add(Document.COLUMN_DOCUMENT_ID, docId);
+ row.add(Document.COLUMN_DISPLAY_NAME, cursor.getString(ImageQuery.DISPLAY_NAME));
+ row.add(Document.COLUMN_SIZE, cursor.getLong(ImageQuery.SIZE));
+ row.add(Document.COLUMN_MIME_TYPE, cursor.getString(ImageQuery.MIME_TYPE));
+ row.add(Document.COLUMN_LAST_MODIFIED,
+ cursor.getLong(ImageQuery.DATE_MODIFIED) * DateUtils.SECOND_IN_MILLIS);
+ row.add(Document.COLUMN_FLAGS, Document.FLAG_SUPPORTS_THUMBNAIL);
+ }
+
+ private interface VideosBucketQuery {
+ final String[] PROJECTION = new String[] {
+ VideoColumns.BUCKET_ID,
+ VideoColumns.BUCKET_DISPLAY_NAME,
+ VideoColumns.DATE_MODIFIED };
+ final String SORT_ORDER = VideoColumns.BUCKET_ID + ", " + VideoColumns.DATE_MODIFIED
+ + " DESC";
+
+ final int BUCKET_ID = 0;
+ final int BUCKET_DISPLAY_NAME = 1;
+ final int DATE_MODIFIED = 2;
+ }
+
+ private void includeVideosBucket(MatrixCursor result, Cursor cursor) {
+ final long id = cursor.getLong(VideosBucketQuery.BUCKET_ID);
+ final String docId = getDocIdForIdent(TYPE_VIDEOS_BUCKET, id);
+
+ final RowBuilder row = result.newRow();
+ row.add(Document.COLUMN_DOCUMENT_ID, docId);
+ row.add(Document.COLUMN_DISPLAY_NAME,
+ cursor.getString(VideosBucketQuery.BUCKET_DISPLAY_NAME));
+ row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ row.add(Document.COLUMN_LAST_MODIFIED,
+ cursor.getLong(VideosBucketQuery.DATE_MODIFIED) * DateUtils.SECOND_IN_MILLIS);
+ row.add(Document.COLUMN_FLAGS, Document.FLAG_DIR_PREFERS_GRID
+ | Document.FLAG_SUPPORTS_THUMBNAIL | Document.FLAG_DIR_PREFERS_LAST_MODIFIED
+ | Document.FLAG_DIR_HIDE_GRID_TITLES);
+ }
+
+ private interface VideoQuery {
+ final String[] PROJECTION = new String[] {
+ VideoColumns._ID,
+ VideoColumns.DISPLAY_NAME,
+ VideoColumns.MIME_TYPE,
+ VideoColumns.SIZE,
+ VideoColumns.DATE_MODIFIED };
+
+ final int _ID = 0;
+ final int DISPLAY_NAME = 1;
+ final int MIME_TYPE = 2;
+ final int SIZE = 3;
+ final int DATE_MODIFIED = 4;
+ }
+
+ private void includeVideo(MatrixCursor result, Cursor cursor) {
+ final long id = cursor.getLong(VideoQuery._ID);
+ final String docId = getDocIdForIdent(TYPE_VIDEO, id);
+
+ final RowBuilder row = result.newRow();
+ row.add(Document.COLUMN_DOCUMENT_ID, docId);
+ row.add(Document.COLUMN_DISPLAY_NAME, cursor.getString(VideoQuery.DISPLAY_NAME));
+ row.add(Document.COLUMN_SIZE, cursor.getLong(VideoQuery.SIZE));
+ row.add(Document.COLUMN_MIME_TYPE, cursor.getString(VideoQuery.MIME_TYPE));
+ row.add(Document.COLUMN_LAST_MODIFIED,
+ cursor.getLong(VideoQuery.DATE_MODIFIED) * DateUtils.SECOND_IN_MILLIS);
+ row.add(Document.COLUMN_FLAGS, Document.FLAG_SUPPORTS_THUMBNAIL);
+ }
+
+ private interface ArtistQuery {
+ final String[] PROJECTION = new String[] {
+ BaseColumns._ID,
+ ArtistColumns.ARTIST };
+
+ final int _ID = 0;
+ final int ARTIST = 1;
+ }
+
+ private void includeArtist(MatrixCursor result, Cursor cursor) {
+ final long id = cursor.getLong(ArtistQuery._ID);
+ final String docId = getDocIdForIdent(TYPE_ARTIST, id);
+
+ final RowBuilder row = result.newRow();
+ row.add(Document.COLUMN_DOCUMENT_ID, docId);
+ row.add(Document.COLUMN_DISPLAY_NAME, cursor.getString(ArtistQuery.ARTIST));
+ row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ }
+
+ private interface AlbumQuery {
+ final String[] PROJECTION = new String[] {
+ BaseColumns._ID,
+ AlbumColumns.ALBUM };
+
+ final int _ID = 0;
+ final int ALBUM = 1;
+ }
+
+ private void includeAlbum(MatrixCursor result, Cursor cursor) {
+ final long id = cursor.getLong(AlbumQuery._ID);
+ final String docId = getDocIdForIdent(TYPE_ALBUM, id);
+
+ final RowBuilder row = result.newRow();
+ row.add(Document.COLUMN_DOCUMENT_ID, docId);
+ row.add(Document.COLUMN_DISPLAY_NAME, cursor.getString(AlbumQuery.ALBUM));
+ row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ }
+
+ private interface SongQuery {
+ final String[] PROJECTION = new String[] {
+ AudioColumns._ID,
+ AudioColumns.TITLE,
+ AudioColumns.MIME_TYPE,
+ AudioColumns.SIZE,
+ AudioColumns.DATE_MODIFIED };
+
+ final int _ID = 0;
+ final int TITLE = 1;
+ final int MIME_TYPE = 2;
+ final int SIZE = 3;
+ final int DATE_MODIFIED = 4;
+ }
+
+ private void includeAudio(MatrixCursor result, Cursor cursor) {
+ final long id = cursor.getLong(SongQuery._ID);
+ final String docId = getDocIdForIdent(TYPE_AUDIO, id);
+
+ final RowBuilder row = result.newRow();
+ row.add(Document.COLUMN_DOCUMENT_ID, docId);
+ row.add(Document.COLUMN_DISPLAY_NAME, cursor.getString(SongQuery.TITLE));
+ row.add(Document.COLUMN_SIZE, cursor.getLong(SongQuery.SIZE));
+ row.add(Document.COLUMN_MIME_TYPE, cursor.getString(SongQuery.MIME_TYPE));
+ row.add(Document.COLUMN_LAST_MODIFIED,
+ cursor.getLong(SongQuery.DATE_MODIFIED) * DateUtils.SECOND_IN_MILLIS);
+ }
+
+ private interface ImagesBucketThumbnailQuery {
+ final String[] PROJECTION = new String[] {
+ ImageColumns._ID,
+ ImageColumns.BUCKET_ID,
+ ImageColumns.DATE_MODIFIED };
+
+ final int _ID = 0;
+ final int BUCKET_ID = 1;
+ final int DATE_MODIFIED = 2;
+ }
+
+ private long getImageForBucketCleared(long bucketId) throws FileNotFoundException {
+ final ContentResolver resolver = getContext().getContentResolver();
+ Cursor cursor = null;
+ try {
+ cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+ ImagesBucketThumbnailQuery.PROJECTION, ImageColumns.BUCKET_ID + "=" + bucketId,
+ null, ImageColumns.DATE_MODIFIED + " DESC");
+ if (cursor.moveToFirst()) {
+ return cursor.getLong(ImagesBucketThumbnailQuery._ID);
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+ throw new FileNotFoundException("No video found for bucket");
+ }
+
+ private interface ImageThumbnailQuery {
+ final String[] PROJECTION = new String[] {
+ Images.Thumbnails.DATA };
+
+ final int _DATA = 0;
+ }
+
+ private ParcelFileDescriptor openImageThumbnailCleared(long id, CancellationSignal signal)
+ throws FileNotFoundException {
+ final ContentResolver resolver = getContext().getContentResolver();
+
+ Cursor cursor = null;
+ try {
+ cursor = resolver.query(Images.Thumbnails.EXTERNAL_CONTENT_URI,
+ ImageThumbnailQuery.PROJECTION, Images.Thumbnails.IMAGE_ID + "=" + id, null,
+ null, signal);
+ if (cursor.moveToFirst()) {
+ final String data = cursor.getString(ImageThumbnailQuery._DATA);
+ return ParcelFileDescriptor.open(
+ new File(data), ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+ return null;
+ }
+
+ private AssetFileDescriptor openOrCreateImageThumbnailCleared(
+ long id, CancellationSignal signal) throws FileNotFoundException {
+ final ContentResolver resolver = getContext().getContentResolver();
+
+ ParcelFileDescriptor pfd = openImageThumbnailCleared(id, signal);
+ if (pfd == null) {
+ // No thumbnail yet, so generate. This is messy, since we drop the
+ // Bitmap on the floor, but its the least-complicated way.
+ final BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inJustDecodeBounds = true;
+ Images.Thumbnails.getThumbnail(resolver, id, Images.Thumbnails.MINI_KIND, opts);
+
+ pfd = openImageThumbnailCleared(id, signal);
+ }
+
+ if (pfd == null) {
+ // Phoey, fallback to full image
+ final Uri fullUri = ContentUris.withAppendedId(Images.Media.EXTERNAL_CONTENT_URI, id);
+ pfd = resolver.openFileDescriptor(fullUri, "r", signal);
+ }
+
+ final int orientation = queryOrientationForImage(id, signal);
+ final Bundle extras;
+ if (orientation != 0) {
+ extras = new Bundle(1);
+ extras.putInt(DocumentsContract.EXTRA_ORIENTATION, orientation);
+ } else {
+ extras = null;
+ }
+
+ return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
+ }
+
+ private interface VideosBucketThumbnailQuery {
+ final String[] PROJECTION = new String[] {
+ VideoColumns._ID,
+ VideoColumns.BUCKET_ID,
+ VideoColumns.DATE_MODIFIED };
+
+ final int _ID = 0;
+ final int BUCKET_ID = 1;
+ final int DATE_MODIFIED = 2;
+ }
+
+ private long getVideoForBucketCleared(long bucketId)
+ throws FileNotFoundException {
+ final ContentResolver resolver = getContext().getContentResolver();
+ Cursor cursor = null;
+ try {
+ cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+ VideosBucketThumbnailQuery.PROJECTION, VideoColumns.BUCKET_ID + "=" + bucketId,
+ null, VideoColumns.DATE_MODIFIED + " DESC");
+ if (cursor.moveToFirst()) {
+ return cursor.getLong(VideosBucketThumbnailQuery._ID);
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+ throw new FileNotFoundException("No video found for bucket");
+ }
+
+ private interface VideoThumbnailQuery {
+ final String[] PROJECTION = new String[] {
+ Video.Thumbnails.DATA };
+
+ final int _DATA = 0;
+ }
+
+ private AssetFileDescriptor openVideoThumbnailCleared(long id, CancellationSignal signal)
+ throws FileNotFoundException {
+ final ContentResolver resolver = getContext().getContentResolver();
+ Cursor cursor = null;
+ try {
+ cursor = resolver.query(Video.Thumbnails.EXTERNAL_CONTENT_URI,
+ VideoThumbnailQuery.PROJECTION, Video.Thumbnails.VIDEO_ID + "=" + id, null,
+ null, signal);
+ if (cursor.moveToFirst()) {
+ final String data = cursor.getString(VideoThumbnailQuery._DATA);
+ return new AssetFileDescriptor(ParcelFileDescriptor.open(
+ new File(data), ParcelFileDescriptor.MODE_READ_ONLY), 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH);
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+ return null;
+ }
+
+ private AssetFileDescriptor openOrCreateVideoThumbnailCleared(
+ long id, CancellationSignal signal) throws FileNotFoundException {
+ final ContentResolver resolver = getContext().getContentResolver();
+
+ AssetFileDescriptor afd = openVideoThumbnailCleared(id, signal);
+ if (afd == null) {
+ // No thumbnail yet, so generate. This is messy, since we drop the
+ // Bitmap on the floor, but its the least-complicated way.
+ final BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inJustDecodeBounds = true;
+ Video.Thumbnails.getThumbnail(resolver, id, Video.Thumbnails.MINI_KIND, opts);
+
+ afd = openVideoThumbnailCleared(id, signal);
+ }
+
+ return afd;
+ }
+
+ private interface ImageOrientationQuery {
+ final String[] PROJECTION = new String[] {
+ ImageColumns.ORIENTATION };
+
+ final int ORIENTATION = 0;
+ }
+
+ private int queryOrientationForImage(long id, CancellationSignal signal) {
+ final ContentResolver resolver = getContext().getContentResolver();
+
+ Cursor cursor = null;
+ try {
+ cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+ ImageOrientationQuery.PROJECTION, ImageColumns._ID + "=" + id, null, null,
+ signal);
+ if (cursor.moveToFirst()) {
+ return cursor.getInt(ImageOrientationQuery.ORIENTATION);
+ } else {
+ Log.w(TAG, "Missing orientation data for " + id);
+ return 0;
+ }
+ } finally {
+ IoUtils.closeQuietly(cursor);
+ }
+ }
+}
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 3e7ee05..9739893 100755
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -38,6 +38,7 @@
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.UriMatcher;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.Cursor;
@@ -59,7 +60,6 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
-import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
@@ -84,6 +84,12 @@
import android.text.format.DateUtils;
import android.util.Log;
+import libcore.io.ErrnoException;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+import libcore.io.OsConstants;
+import libcore.io.StructStat;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -101,12 +107,6 @@
import java.util.PriorityQueue;
import java.util.Stack;
-import libcore.io.ErrnoException;
-import libcore.io.IoUtils;
-import libcore.io.Libcore;
-import libcore.io.OsConstants;
-import libcore.io.StructStat;
-
/**
* Media content provider. See {@link android.provider.MediaStore} for details.
* Separate databases are kept for each external storage card we see (using the
@@ -139,6 +139,8 @@
}
}
+ private StorageManager mStorageManager;
+
// In memory cache of path<->id mappings, to speed up inserts during media scan
HashMap<String, Long> mDirectoryCache = new HashMap<String, Long>();
@@ -232,6 +234,8 @@
private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart");
+ private static final String CANONICAL = "canonical";
+
private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -265,7 +269,7 @@
// We do this to avoid deleting files if the volume is remounted while
// we are still processing the unmount event.
ContentValues values = new ContentValues();
- values.put(Files.FileColumns.DATA, "");
+ values.putNull(Files.FileColumns.DATA);
String where = FileColumns.STORAGE_ID + "=?";
String[] whereArgs = new String[] { Integer.toString(storage.getStorageId()) };
database.mNumUpdates++;
@@ -546,6 +550,8 @@
public boolean onCreate() {
final Context context = getContext();
+ mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+
sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " +
MediaStore.Audio.Albums._ID);
sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album");
@@ -635,7 +641,7 @@
d = (ThumbData)mThumbRequestStack.pop();
}
- makeThumbInternal(d);
+ IoUtils.closeQuietly(makeThumbInternal(d));
synchronized (mPendingThumbs) {
mPendingThumbs.remove(d.path);
}
@@ -1821,6 +1827,19 @@
db.execSQL("ALTER TABLE log_tmp RENAME TO log;");
}
+ if (fromVersion < 700) {
+ // fix datetaken fields that were added with an incorrect timestamp
+ // datetaken needs to be in milliseconds, so should generally be a few orders of
+ // magnitude larger than date_modified. If it's within the same order of magnitude, it
+ // is probably wrong.
+ // (this could do the wrong thing if your picture was actually taken before ~3/21/1970)
+ db.execSQL("UPDATE files set datetaken=date_modified*1000"
+ + " WHERE date_modified IS NOT NULL"
+ + " AND datetaken IS NOT NULL"
+ + " AND datetaken<date_modified*5;");
+ }
+
+
sanityCheck(db, fromVersion);
long elapsedSeconds = (SystemClock.currentTimeMicro() - startTime) / 1000000;
logToDb(db, "Database upgraded from version " + fromVersion + " to " + toVersion
@@ -2117,10 +2136,100 @@
}
return true;
}
+
+ @Override
+ public Uri canonicalize(Uri uri) {
+ int match = URI_MATCHER.match(uri);
+
+ // only support canonicalizing specific audio Uris
+ if (match != AUDIO_MEDIA_ID) {
+ return null;
+ }
+
+ Cursor c = query(uri, null, null, null, null);
+ if (c == null) {
+ return null;
+ }
+ if (c.getCount() != 1 || !c.moveToNext()) {
+ c.close();
+ return null;
+ }
+
+ // Construct a canonical Uri by tacking on some query parameters
+ Uri.Builder builder = uri.buildUpon();
+ builder.appendQueryParameter(CANONICAL, "1");
+ String title = c.getString(c.getColumnIndex(MediaStore.Audio.Media.TITLE));
+ c.close();
+ if (TextUtils.isEmpty(title)) {
+ return null;
+ }
+ builder.appendQueryParameter(MediaStore.Audio.Media.TITLE, title);
+ Uri newUri = builder.build();
+ return newUri;
+ }
+
+ @Override
+ public Uri uncanonicalize(Uri uri) {
+ if (uri != null && "1".equals(uri.getQueryParameter(CANONICAL))) {
+ int match = URI_MATCHER.match(uri);
+ if (match != AUDIO_MEDIA_ID) {
+ // this type of canonical Uri is not supported
+ return null;
+ }
+ String titleFromUri = uri.getQueryParameter(MediaStore.Audio.Media.TITLE);
+ if (titleFromUri == null) {
+ // the required parameter is missing
+ return null;
+ }
+ // clear the query parameters, we don't need them anymore
+ uri = uri.buildUpon().clearQuery().build();
+
+ Cursor c = query(uri, null, null, null, null);
+
+ int titleIdx = c.getColumnIndex(MediaStore.Audio.Media.TITLE);
+ if (c != null && c.getCount() == 1 && c.moveToNext() &&
+ titleFromUri.equals(c.getString(titleIdx))) {
+ // the result matched perfectly
+ c.close();
+ return uri;
+ }
+
+ c.close();
+ // do a lookup by title
+ Uri newUri = MediaStore.Audio.Media.getContentUri(uri.getPathSegments().get(0));
+
+ c = query(newUri, null, MediaStore.Audio.Media.TITLE + "=?",
+ new String[] {titleFromUri}, null);
+ if (c == null) {
+ return null;
+ }
+ if (!c.moveToNext()) {
+ c.close();
+ return null;
+ }
+ // get the first matching entry and return a Uri for it
+ long id = c.getLong(c.getColumnIndex(MediaStore.Audio.Media._ID));
+ c.close();
+ return ContentUris.withAppendedId(newUri, id);
+ }
+ return uri;
+ }
+
+ private Uri safeUncanonicalize(Uri uri) {
+ Uri newUri = uncanonicalize(uri);
+ if (newUri != null) {
+ return newUri;
+ }
+ return uri;
+ }
+
@SuppressWarnings("fallthrough")
@Override
public Cursor query(Uri uri, String[] projectionIn, String selection,
String[] selectionArgs, String sort) {
+
+ uri = safeUncanonicalize(uri);
+
int table = URI_MATCHER.match(uri);
List<String> prependArgs = new ArrayList<String>();
@@ -3250,6 +3359,8 @@
private Uri insertInternal(Uri uri, int match, ContentValues initialValues,
ArrayList<Long> notifyRowIds) {
+ final String volumeName = getVolumeName(uri);
+
long rowId;
if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues);
@@ -3290,8 +3401,10 @@
rowId = insertFile(helper, uri, initialValues,
FileColumns.MEDIA_TYPE_IMAGE, true, notifyRowIds);
if (rowId > 0) {
+ MediaDocumentsProvider.onMediaStoreInsert(
+ getContext(), volumeName, FileColumns.MEDIA_TYPE_IMAGE, rowId);
newUri = ContentUris.withAppendedId(
- Images.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
+ Images.Media.getContentUri(volumeName), rowId);
}
break;
}
@@ -3304,7 +3417,7 @@
rowId = db.insert("thumbnails", "name", values);
if (rowId > 0) {
newUri = ContentUris.withAppendedId(Images.Thumbnails.
- getContentUri(uri.getPathSegments().get(0)), rowId);
+ getContentUri(volumeName), rowId);
}
break;
}
@@ -3317,7 +3430,7 @@
rowId = db.insert("videothumbnails", "name", values);
if (rowId > 0) {
newUri = ContentUris.withAppendedId(Video.Thumbnails.
- getContentUri(uri.getPathSegments().get(0)), rowId);
+ getContentUri(volumeName), rowId);
}
break;
}
@@ -3326,7 +3439,10 @@
rowId = insertFile(helper, uri, initialValues,
FileColumns.MEDIA_TYPE_AUDIO, true, notifyRowIds);
if (rowId > 0) {
- newUri = ContentUris.withAppendedId(Audio.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
+ MediaDocumentsProvider.onMediaStoreInsert(
+ getContext(), volumeName, FileColumns.MEDIA_TYPE_AUDIO, rowId);
+ newUri = ContentUris.withAppendedId(
+ Audio.Media.getContentUri(volumeName), rowId);
if (genre != null) {
updateGenre(rowId, genre);
}
@@ -3363,7 +3479,8 @@
helper.mNumInserts++;
rowId = db.insert("audio_genres", "audio_id", initialValues);
if (rowId > 0) {
- newUri = ContentUris.withAppendedId(Audio.Genres.getContentUri(uri.getPathSegments().get(0)), rowId);
+ newUri = ContentUris.withAppendedId(
+ Audio.Genres.getContentUri(volumeName), rowId);
}
break;
}
@@ -3386,7 +3503,8 @@
rowId = insertFile(helper, uri, values,
FileColumns.MEDIA_TYPE_PLAYLIST, true, notifyRowIds);
if (rowId > 0) {
- newUri = ContentUris.withAppendedId(Audio.Playlists.getContentUri(uri.getPathSegments().get(0)), rowId);
+ newUri = ContentUris.withAppendedId(
+ Audio.Playlists.getContentUri(volumeName), rowId);
}
break;
}
@@ -3408,8 +3526,10 @@
rowId = insertFile(helper, uri, initialValues,
FileColumns.MEDIA_TYPE_VIDEO, true, notifyRowIds);
if (rowId > 0) {
- newUri = ContentUris.withAppendedId(Video.Media.getContentUri(
- uri.getPathSegments().get(0)), rowId);
+ MediaDocumentsProvider.onMediaStoreInsert(
+ getContext(), volumeName, FileColumns.MEDIA_TYPE_VIDEO, rowId);
+ newUri = ContentUris.withAppendedId(
+ Video.Media.getContentUri(volumeName), rowId);
}
break;
}
@@ -3463,7 +3583,7 @@
rowId = insertFile(helper, uri, initialValues,
FileColumns.MEDIA_TYPE_NONE, true, notifyRowIds);
if (rowId > 0) {
- newUri = Files.getContentUri(uri.getPathSegments().get(0), rowId);
+ newUri = Files.getContentUri(volumeName, rowId);
}
break;
@@ -3472,7 +3592,7 @@
rowId = insertFile(helper, uri, initialValues,
FileColumns.MEDIA_TYPE_NONE, false, notifyRowIds);
if (rowId > 0) {
- newUri = Files.getMtpObjectsUri(uri.getPathSegments().get(0), rowId);
+ newUri = Files.getMtpObjectsUri(volumeName, rowId);
}
break;
@@ -3812,6 +3932,7 @@
@Override
public int delete(Uri uri, String userWhere, String[] whereArgs) {
+ uri = safeUncanonicalize(uri);
int count;
int match = URI_MATCHER.match(uri);
@@ -3850,6 +3971,8 @@
}
}
} else {
+ final String volumeName = getVolumeName(uri);
+
DatabaseHelper database = getDatabaseForUri(uri);
if (database == null) {
throw new UnsupportedOperationException(
@@ -3860,7 +3983,6 @@
synchronized (sGetTableAndWhereParam) {
getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
-
if (sGetTableAndWhereParam.table.equals("files")) {
String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA);
if (deleteparam == null || ! deleteparam.equals("false")) {
@@ -3871,30 +3993,37 @@
String [] idvalue = new String[] { "" };
String [] playlistvalues = new String[] { "", "" };
while (c.moveToNext()) {
- int mediatype = c.getInt(0);
- if (mediatype == FileColumns.MEDIA_TYPE_IMAGE) {
- try {
- Libcore.os.remove(c.getString(1));
- idvalue[0] = "" + c.getLong(2);
- database.mNumQueries++;
- Cursor cc = db.query("thumbnails", sDataOnlyColumn,
- "image_id=?", idvalue, null, null, null);
- while (cc.moveToNext()) {
- Libcore.os.remove(cc.getString(0));
- }
- cc.close();
- database.mNumDeletes++;
- db.delete("thumbnails", "image_id=?", idvalue);
- } catch (ErrnoException e) {
+ final int mediaType = c.getInt(0);
+ final String data = c.getString(1);
+ final long id = c.getLong(2);
+
+ if (mediaType == FileColumns.MEDIA_TYPE_IMAGE) {
+ deleteIfAllowed(uri, data);
+ MediaDocumentsProvider.onMediaStoreDelete(getContext(), volumeName,
+ FileColumns.MEDIA_TYPE_IMAGE, id);
+
+ idvalue[0] = String.valueOf(id);
+ database.mNumQueries++;
+ Cursor cc = db.query("thumbnails", sDataOnlyColumn,
+ "image_id=?", idvalue, null, null, null);
+ while (cc.moveToNext()) {
+ deleteIfAllowed(uri, cc.getString(0));
}
- } else if (mediatype == FileColumns.MEDIA_TYPE_VIDEO) {
- try {
- Libcore.os.remove(c.getString(1));
- } catch (ErrnoException e) {
- }
- } else if (mediatype == FileColumns.MEDIA_TYPE_AUDIO) {
+ cc.close();
+ database.mNumDeletes++;
+ db.delete("thumbnails", "image_id=?", idvalue);
+
+ } else if (mediaType == FileColumns.MEDIA_TYPE_VIDEO) {
+ deleteIfAllowed(uri, data);
+ MediaDocumentsProvider.onMediaStoreDelete(getContext(), volumeName,
+ FileColumns.MEDIA_TYPE_VIDEO, id);
+
+ } else if (mediaType == FileColumns.MEDIA_TYPE_AUDIO) {
if (!database.mInternal) {
- idvalue[0] = "" + c.getLong(2);
+ MediaDocumentsProvider.onMediaStoreDelete(getContext(),
+ volumeName, FileColumns.MEDIA_TYPE_AUDIO, id);
+
+ idvalue[0] = String.valueOf(id);
database.mNumDeletes += 2; // also count the one below
db.delete("audio_genres_map", "audio_id=?", idvalue);
// for each playlist that the item appears in, move
@@ -3914,7 +4043,7 @@
cc.close();
db.delete("audio_playlists_map", "audio_id=?", idvalue);
}
- } else if (mediatype == FileColumns.MEDIA_TYPE_PLAYLIST) {
+ } else if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
// TODO, maybe: remove the audio_playlists_cleanup trigger and implement
// it functionality here (clean up the playlist map)
}
@@ -3951,10 +4080,7 @@
sGetTableAndWhereParam.where, whereArgs, null, null, null);
if (c != null) {
while (c.moveToNext()) {
- try {
- Libcore.os.remove(c.getString(0));
- } catch (ErrnoException e) {
- }
+ deleteIfAllowed(uri, c.getString(0));
}
c.close();
}
@@ -3969,12 +4095,12 @@
sGetTableAndWhereParam.where, whereArgs);
break;
}
+
// Since there are multiple Uris that can refer to the same files
// and deletes can affect other objects in storage (like subdirectories
// or playlists) we will notify a change on the entire volume to make
// sure no listeners miss the notification.
- String volume = uri.getPathSegments().get(0);
- Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volume);
+ Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volumeName);
getContext().getContentResolver().notifyChange(notifyUri, null);
}
}
@@ -3994,6 +4120,7 @@
@Override
public int update(Uri uri, ContentValues initialValues, String userWhere,
String[] whereArgs) {
+ uri = safeUncanonicalize(uri);
int count;
// Log.v(TAG, "update for uri="+uri+", initValues="+initialValues);
int match = URI_MATCHER.match(uri);
@@ -4328,6 +4455,7 @@
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
+ uri = safeUncanonicalize(uri);
ParcelFileDescriptor pfd = null;
if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) {
@@ -4440,10 +4568,33 @@
*/
private ParcelFileDescriptor openFileAndEnforcePathPermissionsHelper(Uri uri, String mode)
throws FileNotFoundException {
- final int modeBits = ContentResolver.modeToMode(uri, mode);
- final boolean isWrite = (modeBits & MODE_WRITE_ONLY) != 0;
+ final int modeBits = ParcelFileDescriptor.parseMode(mode);
File file = queryForDataFile(uri);
+
+ checkAccess(uri, file, modeBits);
+
+ // Bypass emulation layer when file is opened for reading, but only
+ // when opening read-only and we have an exact match.
+ if (modeBits == MODE_READ_ONLY) {
+ file = Environment.maybeTranslateEmulatedPathToInternal(file);
+ }
+
+ return ParcelFileDescriptor.open(file, modeBits);
+ }
+
+ private void deleteIfAllowed(Uri uri, String path) {
+ try {
+ File file = new File(path);
+ checkAccess(uri, file, ParcelFileDescriptor.MODE_WRITE_ONLY);
+ file.delete();
+ } catch (Exception e) {
+ Log.e(TAG, "Couldn't delete " + path);
+ }
+ }
+
+ private void checkAccess(Uri uri, File file, int modeBits) throws FileNotFoundException {
+ final boolean isWrite = (modeBits & MODE_WRITE_ONLY) != 0;
final String path;
try {
path = file.getCanonicalPath();
@@ -4452,19 +4603,21 @@
}
if (path.startsWith(sExternalPath) || path.startsWith(sLegacyPath)) {
- getContext().enforceCallingOrSelfPermission(
- READ_EXTERNAL_STORAGE, "External path: " + path);
+ Context c = getContext();
+ if (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ != PackageManager.PERMISSION_GRANTED) {
+ c.enforceCallingOrSelfPermission(
+ READ_EXTERNAL_STORAGE, "External path: " + path);
+ }
if (isWrite) {
- getContext().enforceCallingOrSelfPermission(
- WRITE_EXTERNAL_STORAGE, "External path: " + path);
+ if (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ != PackageManager.PERMISSION_GRANTED) {
+ c.enforceCallingOrSelfPermission(
+ WRITE_EXTERNAL_STORAGE, "External path: " + path);
+ }
}
- // Bypass emulation layer when file is opened for reading, but only
- // when opening read-only and we have an exact match.
- if (modeBits == MODE_READ_ONLY) {
- file = Environment.maybeTranslateEmulatedPathToInternal(file);
- }
} else if (path.startsWith(sCachePath)) {
getContext().enforceCallingOrSelfPermission(
ACCESS_CACHE_FILESYSTEM, "Cache path: " + path);
@@ -4478,8 +4631,6 @@
} else {
checkWorldReadAccess(path);
}
-
- return ParcelFileDescriptor.open(file, modeBits);
}
private boolean isSecondaryExternalPath(String path) {
@@ -5080,16 +5231,15 @@
false, mObjectRemovedCallback);
} else if (EXTERNAL_VOLUME.equals(volume)) {
if (Environment.isExternalStorageRemovable()) {
- String path = mExternalStoragePaths[0];
- int volumeID = FileUtils.getFatVolumeId(path);
- if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID);
+ final StorageVolume actualVolume = mStorageManager.getPrimaryVolume();
+ final int volumeId = actualVolume.getFatVolumeId();
// Must check for failure!
// If the volume is not (yet) mounted, this will create a new
// external-ffffffff.db database instead of the one we expect. Then, if
// android.process.media is later killed and respawned, the real external
// database will be attached, containing stale records, or worse, be empty.
- if (volumeID == -1) {
+ if (volumeId == -1) {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
@@ -5108,10 +5258,10 @@
}
// generate database name based on volume ID
- String dbName = "external-" + Integer.toHexString(volumeID) + ".db";
+ String dbName = "external-" + Integer.toHexString(volumeId) + ".db";
helper = new DatabaseHelper(context, dbName, false,
false, mObjectRemovedCallback);
- mVolumeId = volumeID;
+ mVolumeId = volumeId;
} else {
// external database name should be EXTERNAL_DATABASE_NAME
// however earlier releases used the external-XXXXXXXX.db naming
@@ -5423,6 +5573,15 @@
URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY);
}
+ private static String getVolumeName(Uri uri) {
+ final List<String> segments = uri.getPathSegments();
+ if (segments != null && segments.size() > 0) {
+ return segments.get(0);
+ } else {
+ return null;
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
Collection<DatabaseHelper> foo = mDatabases.values();
diff --git a/src/com/android/providers/media/MediaScannerReceiver.java b/src/com/android/providers/media/MediaScannerReceiver.java
index 0e0d321..89635b9 100644
--- a/src/com/android/providers/media/MediaScannerReceiver.java
+++ b/src/com/android/providers/media/MediaScannerReceiver.java
@@ -25,6 +25,9 @@
import android.os.Environment;
import android.util.Log;
+import java.io.File;
+import java.io.IOException;
+
public class MediaScannerReceiver extends BroadcastReceiver {
private final static String TAG = "MediaScannerReceiver";
@@ -42,6 +45,17 @@
// handle intents related to external storage
String path = uri.getPath();
String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
+ String legacyPath = Environment.getLegacyExternalStorageDirectory().getPath();
+
+ try {
+ path = new File(path).getCanonicalPath();
+ } catch (IOException e) {
+ Log.e(TAG, "couldn't canonicalize " + path);
+ return;
+ }
+ if (path.startsWith(legacyPath)) {
+ path = externalStoragePath + path.substring(legacyPath.length());
+ }
Log.d(TAG, "action: " + action + " path: " + path);
if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
diff --git a/src/com/android/providers/media/RingtonePickerActivity.java b/src/com/android/providers/media/RingtonePickerActivity.java
index c5118e7..3075bbb 100644
--- a/src/com/android/providers/media/RingtonePickerActivity.java
+++ b/src/com/android/providers/media/RingtonePickerActivity.java
@@ -26,6 +26,7 @@
import android.os.Handler;
import android.provider.MediaStore;
import android.provider.Settings;
+import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
@@ -44,6 +45,8 @@
AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener,
AlertController.AlertParams.OnPrepareListViewListener {
+ private static final int POS_UNKNOWN = -1;
+
private static final String TAG = "RingtonePickerActivity";
private static final int DELAY_MS_SELECTION_PLAYED = 300;
@@ -57,16 +60,16 @@
private Handler mHandler;
/** The position in the list of the 'Silent' item. */
- private int mSilentPos = -1;
+ private int mSilentPos = POS_UNKNOWN;
/** The position in the list of the 'Default' item. */
- private int mDefaultRingtonePos = -1;
+ private int mDefaultRingtonePos = POS_UNKNOWN;
/** The position in the list of the last clicked item. */
- private int mClickedPos = -1;
+ private int mClickedPos = POS_UNKNOWN;
/** The position in the list of the ringtone to sample. */
- private int mSampleRingtonePos = -1;
+ private int mSampleRingtonePos = POS_UNKNOWN;
/** Whether this list has the 'Silent' item. */
private boolean mHasSilentItem;
@@ -90,6 +93,18 @@
*/
private Ringtone mDefaultRingtone;
+ /**
+ * The ringtone that's currently playing, unless the currently playing one is the default
+ * ringtone.
+ */
+ private Ringtone mCurrentRingtone;
+
+ /**
+ * Keep the currently playing ringtone around when changing orientation, so that it
+ * can be stopped later, after the activity is recreated.
+ */
+ private static Ringtone sPlayingRingtone;
+
private DialogInterface.OnClickListener mRingtoneClickListener =
new DialogInterface.OnClickListener() {
@@ -125,7 +140,7 @@
}
if (savedInstanceState != null) {
- mClickedPos = savedInstanceState.getInt(SAVE_CLICKED_POS, -1);
+ mClickedPos = savedInstanceState.getInt(SAVE_CLICKED_POS, POS_UNKNOWN);
}
// Get whether to show the 'Silent' item
mHasSilentItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
@@ -133,11 +148,6 @@
// Give the Activity so it can do managed queries
mRingtoneManager = new RingtoneManager(this);
- // Get whether to include DRM ringtones
- final boolean includeDrm = intent.getBooleanExtra(
- RingtoneManager.EXTRA_RINGTONE_INCLUDE_DRM, true);
- mRingtoneManager.setIncludeDrm(includeDrm);
-
// Get the types of ringtones to show
mType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1);
if (mType != -1) {
@@ -172,7 +182,6 @@
setupAlert();
}
-
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -184,7 +193,7 @@
if (mHasDefaultItem) {
mDefaultRingtonePos = addDefaultRingtoneItem(listView);
- if (RingtoneManager.isDefault(mExistingUri)) {
+ if (mClickedPos == POS_UNKNOWN && RingtoneManager.isDefault(mExistingUri)) {
mClickedPos = mDefaultRingtonePos;
}
}
@@ -193,12 +202,12 @@
mSilentPos = addSilentItem(listView);
// The 'Silent' item should use a null Uri
- if (mExistingUri == null) {
+ if (mClickedPos == POS_UNKNOWN && mExistingUri == null) {
mClickedPos = mSilentPos;
}
}
- if (mClickedPos == -1) {
+ if (mClickedPos == POS_UNKNOWN) {
mClickedPos = getListPosition(mRingtoneManager.getRingtonePosition(mExistingUri));
}
@@ -292,21 +301,11 @@
}
public void run() {
-
+ stopAnyPlayingRingtone();
if (mSampleRingtonePos == mSilentPos) {
- mRingtoneManager.stopPreviousRingtone();
return;
}
- /*
- * Stop the default ringtone, if it's playing (other ringtones will be
- * stopped by the RingtoneManager when we get another Ringtone from it.
- */
- if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
- mDefaultRingtone.stop();
- mDefaultRingtone = null;
- }
-
Ringtone ringtone;
if (mSampleRingtonePos == mDefaultRingtonePos) {
if (mDefaultRingtone == null) {
@@ -320,17 +319,10 @@
mDefaultRingtone.setStreamType(mRingtoneManager.inferStreamType());
}
ringtone = mDefaultRingtone;
-
- /*
- * Normally the non-static RingtoneManager.getRingtone stops the
- * previous ringtone, but we're getting the default ringtone outside
- * of the RingtoneManager instance, so let's stop the previous
- * ringtone manually.
- */
- mRingtoneManager.stopPreviousRingtone();
-
+ mCurrentRingtone = null;
} else {
ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos));
+ mCurrentRingtone = ringtone;
}
if (ringtone != null) {
@@ -341,16 +333,34 @@
@Override
protected void onStop() {
super.onStop();
- stopAnyPlayingRingtone();
+ if (!isChangingConfigurations()) {
+ stopAnyPlayingRingtone();
+ } else {
+ saveAnyPlayingRingtone();
+ }
}
@Override
protected void onPause() {
super.onPause();
- stopAnyPlayingRingtone();
+ if (!isChangingConfigurations()) {
+ stopAnyPlayingRingtone();
+ }
+ }
+
+ private void saveAnyPlayingRingtone() {
+ if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
+ sPlayingRingtone = mDefaultRingtone;
+ } else if (mCurrentRingtone != null && mCurrentRingtone.isPlaying()) {
+ sPlayingRingtone = mCurrentRingtone;
+ }
}
private void stopAnyPlayingRingtone() {
+ if (sPlayingRingtone != null && sPlayingRingtone.isPlaying()) {
+ sPlayingRingtone.stop();
+ }
+ sPlayingRingtone = null;
if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
mDefaultRingtone.stop();
diff --git a/tools/genfiles/genfiles.sh b/tools/genfiles/genfiles.sh
index 2a139a5..0df984a 100755
--- a/tools/genfiles/genfiles.sh
+++ b/tools/genfiles/genfiles.sh
@@ -29,12 +29,6 @@
fi
-if [ "$ANDROID_HOST_OUT" == "" ]
-then
- echo "Couldn't find sqlite3. Please run envsetup/lunch and build."
- exit 1
-fi
-
if [ "$1" == "" ]
then
echo "Usage: $0 <file.db> [external storage root]"
@@ -48,11 +42,14 @@
fi
# generate script to generate directory structure and content
-$ANDROID_HOST_OUT/bin/sqlite3 $1 "select format, media_type, mime_type, _data from files where _data like '"$EXTERNAL"/%';" | {
+sqlite3 $1 "select format, media_type, mime_type, case when substr(_data,-1) is '\' then substr(_data,1,length(_data)-1) else _data end from files where _data like '"$EXTERNAL"/%';" | {
MKDIRS=/tmp/mkdirs$$
CPFILES=/tmp/cpfiles$$
+echo "# create directories" > $MKDIRS
+echo "# copy files" > $CPFILES
+
IFS="|"
while read format mediatype mimetype data;
do
@@ -100,15 +97,19 @@
then
# 3gp
echo "cat /storage/sdcard0/proto.3gp > \"$data\"" >> $CPFILES
- elif [ "$format" == "47362" -a "$mediatype" == "2" ]
+ elif [ "$format" == "47362" ]
then
# ogg
echo "cat /storage/sdcard0/proto.ogg > \"$data\"" >> $CPFILES
+ elif [ "$format" == "47747" ]
+ then
+ # doc
+ echo "cat /storage/sdcard0/proto.doc > \"$data\"" >> $CPFILES
elif [ "$format" == "12288" -a "$mediatype" == "0" ]
then
# unknown type
echo "cat /storage/sdcard0/proto.dat > \"$data\"" >> $CPFILES
- elif [ "$format" == "12289" -a "$mediatype" == "0" ]
+ elif [ "$format" == "12289" ]
then
# directory, ignore
true
@@ -119,7 +120,7 @@
else
echo ignored: $format '|' $mediatype '|' $mimetype '|' $data
fi
- echo mkdir -p \"$(dirname $data)\" >> $MKDIRS
+ echo mkdir -p \"$(dirname "$data")\" >> $MKDIRS
done
sort -u $MKDIRS > mkfiles.sh
@@ -129,7 +130,7 @@
}
# generate playlist files
-$ANDROID_HOST_OUT/bin/sqlite3 $1 "select audio_playlists._data, audio._data from audio_playlists left outer join audio_playlists_map on audio_playlists._id=audio_playlists_map.playlist_id left outer join audio on audio_playlists_map.audio_id=audio._id order by audio_playlists_map.playlist_id,audio_playlists_map.play_order;" | {
+sqlite3 $1 "select audio_playlists._data, audio._data from audio_playlists left outer join audio_playlists_map on audio_playlists._id=audio_playlists_map.playlist_id left outer join audio on audio_playlists_map.audio_id=audio._id order by audio_playlists_map.playlist_id,audio_playlists_map.play_order;" | {
IFS="|"
while read plist entry
diff --git a/tools/genfiles/protos/proto.doc b/tools/genfiles/protos/proto.doc
new file mode 100644
index 0000000..75bc601
--- /dev/null
+++ b/tools/genfiles/protos/proto.doc
Binary files differ