summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--res/layout/item_cloud_video_preview.xml61
-rw-r--r--res/layout/item_video_preview.xml67
-rw-r--r--res/mipmap-hdpi/picker_app_icon.pngbin1323 -> 3065 bytes
-rw-r--r--res/mipmap-mdpi/picker_app_icon.pngbin1048 -> 1824 bytes
-rw-r--r--res/mipmap-xhdpi/picker_app_icon.pngbin2396 -> 4311 bytes
-rw-r--r--res/mipmap-xxhdpi/picker_app_icon.pngbin3284 -> 7108 bytes
-rw-r--r--res/mipmap-xxxhdpi/picker_app_icon.pngbin4462 -> 10540 bytes
-rw-r--r--res/values-af/strings.xml3
-rw-r--r--res/values-am/strings.xml3
-rw-r--r--res/values-ar/strings.xml3
-rw-r--r--res/values-as/strings.xml3
-rw-r--r--res/values-az/strings.xml3
-rw-r--r--res/values-be/strings.xml3
-rw-r--r--res/values-bg/strings.xml3
-rw-r--r--res/values-bs/strings.xml3
-rw-r--r--res/values-ca/strings.xml3
-rw-r--r--res/values-cs/strings.xml3
-rw-r--r--res/values-da/strings.xml3
-rw-r--r--res/values-de/strings.xml3
-rw-r--r--res/values-es-rUS/strings.xml3
-rw-r--r--res/values-es/strings.xml3
-rw-r--r--res/values-et/strings.xml3
-rw-r--r--res/values-fa/strings.xml3
-rw-r--r--res/values-fi/strings.xml3
-rw-r--r--res/values-fr-rCA/strings.xml3
-rw-r--r--res/values-fr/strings.xml3
-rw-r--r--res/values-gl/strings.xml3
-rw-r--r--res/values-gu/strings.xml3
-rw-r--r--res/values-hi/strings.xml3
-rw-r--r--res/values-hr/strings.xml3
-rw-r--r--res/values-hu/strings.xml3
-rw-r--r--res/values-hy/strings.xml3
-rw-r--r--res/values-in/strings.xml3
-rw-r--r--res/values-is/strings.xml3
-rw-r--r--res/values-it/strings.xml3
-rw-r--r--res/values-iw/strings.xml3
-rw-r--r--res/values-kk/strings.xml3
-rw-r--r--res/values-km/strings.xml3
-rw-r--r--res/values-ko/strings.xml3
-rw-r--r--res/values-lo/strings.xml3
-rw-r--r--res/values-lt/strings.xml3
-rw-r--r--res/values-lv/strings.xml3
-rw-r--r--res/values-ms/strings.xml3
-rw-r--r--res/values-my/strings.xml3
-rw-r--r--res/values-nb/strings.xml3
-rw-r--r--res/values-ne/strings.xml3
-rw-r--r--res/values-nl/strings.xml3
-rw-r--r--res/values-or/strings.xml3
-rw-r--r--res/values-pa/strings.xml3
-rw-r--r--res/values-pl/strings.xml3
-rw-r--r--res/values-pt-rPT/strings.xml3
-rw-r--r--res/values-ru/strings.xml3
-rw-r--r--res/values-si/strings.xml3
-rw-r--r--res/values-sl/strings.xml3
-rw-r--r--res/values-sq/strings.xml3
-rw-r--r--res/values-sv/strings.xml3
-rw-r--r--res/values-sw/strings.xml3
-rw-r--r--res/values-ta/strings.xml3
-rw-r--r--res/values-te/strings.xml3
-rw-r--r--res/values-th/strings.xml3
-rw-r--r--res/values-tl/strings.xml3
-rw-r--r--res/values-tr/strings.xml3
-rw-r--r--res/values-uk/strings.xml3
-rw-r--r--res/values-ur/strings.xml3
-rw-r--r--res/values-zh-rCN/strings.xml3
-rw-r--r--res/values-zh-rHK/strings.xml3
-rw-r--r--res/values-zh-rTW/strings.xml3
-rw-r--r--res/values-zu/strings.xml3
-rw-r--r--res/values/drawables.xml21
-rw-r--r--src/com/android/providers/media/DatabaseHelper.java70
-rw-r--r--src/com/android/providers/media/MediaProvider.java41
-rw-r--r--src/com/android/providers/media/photopicker/PhotoPickerActivity.java15
-rw-r--r--src/com/android/providers/media/photopicker/PhotoPickerProvider.java4
-rw-r--r--src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java27
-rw-r--r--src/com/android/providers/media/photopicker/data/ExternalDbFacade.java17
-rw-r--r--src/com/android/providers/media/photopicker/data/ItemsProvider.java27
-rw-r--r--src/com/android/providers/media/photopicker/data/PickerDbFacade.java47
-rw-r--r--src/com/android/providers/media/photopicker/metrics/PhotoPickerUiEventLogger.java17
-rw-r--r--src/com/android/providers/media/photopicker/ui/AlbumsTabFragment.java2
-rw-r--r--src/com/android/providers/media/photopicker/ui/ExoPlayerWrapper.java321
-rw-r--r--src/com/android/providers/media/photopicker/ui/ImageLoader.java83
-rw-r--r--src/com/android/providers/media/photopicker/ui/PlaybackHandler.java124
-rw-r--r--src/com/android/providers/media/photopicker/ui/PreviewAdapter.java38
-rw-r--r--src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java24
-rw-r--r--src/com/android/providers/media/photopicker/util/MimeFilterUtils.java30
-rw-r--r--src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java21
-rw-r--r--src/com/android/providers/media/scan/ModernMediaScanner.java4
-rw-r--r--src/com/android/providers/media/util/DatabaseUtils.java10
-rw-r--r--src/com/android/providers/media/util/FileUtils.java54
-rw-r--r--tests/Android.bp8
-rw-r--r--tests/AndroidManifest.xml5
-rw-r--r--tests/src/com/android/providers/media/IdleServiceTest.java5
-rw-r--r--tests/src/com/android/providers/media/MediaProviderTest.java47
-rw-r--r--tests/src/com/android/providers/media/PickerProviderMediaGenerator.java47
-rw-r--r--tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java4
-rw-r--r--tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java5
-rw-r--r--tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java12
-rw-r--r--tests/src/com/android/providers/media/photopicker/LocalProvider.java5
-rw-r--r--tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java4
-rw-r--r--tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java6
-rw-r--r--tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java16
-rw-r--r--tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java33
-rw-r--r--tests/src/com/android/providers/media/photopicker/util/MimeFilterUtilsTest.java43
-rw-r--r--tests/src/com/android/providers/media/photopicker/viewmodel/PickerViewModelTest.java69
-rw-r--r--tests/src/com/android/providers/media/scan/MediaScannerTest.java5
-rw-r--r--tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java10
-rw-r--r--tests/src/com/android/providers/media/util/FileUtilsTest.java37
107 files changed, 679 insertions, 990 deletions
diff --git a/res/layout/item_cloud_video_preview.xml b/res/layout/item_cloud_video_preview.xml
deleted file mode 100644
index f8c3b6e73..000000000
--- a/res/layout/item_cloud_video_preview.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 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.
- -->
-
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <FrameLayout
- android:id="@+id/preview_player_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center">
-
- <com.google.android.exoplayer2.ui.AspectRatioFrameLayout
- android:id="@+id/preview_player_frame"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center">
-
- <SurfaceView
- android:id="@+id/preview_player_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- </com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
-
- <FrameLayout
- android:id="@+id/preview_player_controls"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center">
-
- <include
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- layout="@layout/preview_video_controls" />
- </FrameLayout>
- </FrameLayout>
-
- <ImageView
- android:id="@+id/preview_video_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:scaleType="fitCenter"
- android:contentDescription="@null" />
-</FrameLayout>
diff --git a/res/layout/item_video_preview.xml b/res/layout/item_video_preview.xml
index 1d7d32d49..f8c3b6e73 100644
--- a/res/layout/item_video_preview.xml
+++ b/res/layout/item_video_preview.xml
@@ -1,36 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 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.
--->
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
-
- <com.google.android.exoplayer2.ui.StyledPlayerView
- android:id="@+id/preview_player_view"
+ <FrameLayout
+ android:id="@+id/preview_player_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="gone"
- app:use_controller="true"
- app:animation_enabled="true"
- app:show_timeout="0"
- app:auto_show="false"
- app:controller_layout_id="@layout/preview_video_controls"/>
+ android:layout_gravity="center">
+
+ <com.google.android.exoplayer2.ui.AspectRatioFrameLayout
+ android:id="@+id/preview_player_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center">
+
+ <SurfaceView
+ android:id="@+id/preview_player_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
+
+ <FrameLayout
+ android:id="@+id/preview_player_controls"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center">
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ layout="@layout/preview_video_controls" />
+ </FrameLayout>
+ </FrameLayout>
<ImageView
android:id="@+id/preview_video_image"
diff --git a/res/mipmap-hdpi/picker_app_icon.png b/res/mipmap-hdpi/picker_app_icon.png
index c911ffcf5..566dd0c49 100644
--- a/res/mipmap-hdpi/picker_app_icon.png
+++ b/res/mipmap-hdpi/picker_app_icon.png
Binary files differ
diff --git a/res/mipmap-mdpi/picker_app_icon.png b/res/mipmap-mdpi/picker_app_icon.png
index 9a0f98afd..bf584de46 100644
--- a/res/mipmap-mdpi/picker_app_icon.png
+++ b/res/mipmap-mdpi/picker_app_icon.png
Binary files differ
diff --git a/res/mipmap-xhdpi/picker_app_icon.png b/res/mipmap-xhdpi/picker_app_icon.png
index ca55edd90..2b41f701f 100644
--- a/res/mipmap-xhdpi/picker_app_icon.png
+++ b/res/mipmap-xhdpi/picker_app_icon.png
Binary files differ
diff --git a/res/mipmap-xxhdpi/picker_app_icon.png b/res/mipmap-xxhdpi/picker_app_icon.png
index 531c94feb..a37b6fab3 100644
--- a/res/mipmap-xxhdpi/picker_app_icon.png
+++ b/res/mipmap-xxhdpi/picker_app_icon.png
Binary files differ
diff --git a/res/mipmap-xxxhdpi/picker_app_icon.png b/res/mipmap-xxxhdpi/picker_app_icon.png
index ce7280527..29d73ecac 100644
--- a/res/mipmap-xxxhdpi/picker_app_icon.png
+++ b/res/mipmap-xxxhdpi/picker_app_icon.png
Binary files differ
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 4094c662c..8195a2fa6 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Plaaslike berging"</string>
<string name="app_label" msgid="9035307001052716210">"Mediaberging"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Media"</string>
<string name="artist_label" msgid="8105600993099120273">"Kunstenaar"</string>
<string name="unknown" msgid="2059049215682829375">"Onbekend"</string>
<string name="root_images" msgid="5861633549189045666">"Prente"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 778965769..735486f36 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"ማህደረመረጃ"</string>
<string name="storage_description" msgid="4081716890357580107">"አካባቢያዊ ማከማቻ"</string>
<string name="app_label" msgid="9035307001052716210">"ማህደረ መረጃ ማከማቻ"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"ሚዲያ"</string>
<string name="artist_label" msgid="8105600993099120273">"አርቲስት"</string>
<string name="unknown" msgid="2059049215682829375">"የማይታወቅ"</string>
<string name="root_images" msgid="5861633549189045666">"ምስሎች"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 745d88a2a..f370817a5 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"الوسائط"</string>
<string name="storage_description" msgid="4081716890357580107">"التخزين المحلي"</string>
<string name="app_label" msgid="9035307001052716210">"تخزين الوسائط"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"الوسائط"</string>
<string name="artist_label" msgid="8105600993099120273">"الفنان"</string>
<string name="unknown" msgid="2059049215682829375">"غير معروف"</string>
<string name="root_images" msgid="5861633549189045666">"الصور"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 3c0539356..a034a1a29 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"মিডিয়া"</string>
<string name="storage_description" msgid="4081716890357580107">"স্থানীয় ষ্ট’ৰেজ"</string>
<string name="app_label" msgid="9035307001052716210">"মিডিয়া ষ্ট’ৰেজ"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"মিডিয়া"</string>
<string name="artist_label" msgid="8105600993099120273">"শিল্পী"</string>
<string name="unknown" msgid="2059049215682829375">"অজ্ঞাত"</string>
<string name="root_images" msgid="5861633549189045666">"প্ৰতিচ্ছবি"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 9192a53b5..19832e3af 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Yerli yaddaş"</string>
<string name="app_label" msgid="9035307001052716210">"Media Yaddaşı"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Media"</string>
<string name="artist_label" msgid="8105600993099120273">"Sənətçi"</string>
<string name="unknown" msgid="2059049215682829375">"Naməlum"</string>
<string name="root_images" msgid="5861633549189045666">"Təsvirlər"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index a5fbc0e85..f01a7dc0b 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Медыя"</string>
<string name="storage_description" msgid="4081716890357580107">"Лакальнае сховішча"</string>
<string name="app_label" msgid="9035307001052716210">"Медыясховішча"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Мультымедыя"</string>
<string name="artist_label" msgid="8105600993099120273">"Выканаўца"</string>
<string name="unknown" msgid="2059049215682829375">"Невядома"</string>
<string name="root_images" msgid="5861633549189045666">"Відарысы"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index d85352d7c..c2d966c2f 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Мултимедия"</string>
<string name="storage_description" msgid="4081716890357580107">"Локално хранилище"</string>
<string name="app_label" msgid="9035307001052716210">"Мултимедийно хранилище"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Мултимедия"</string>
<string name="artist_label" msgid="8105600993099120273">"Изпълнител"</string>
<string name="unknown" msgid="2059049215682829375">"Неизвестно"</string>
<string name="root_images" msgid="5861633549189045666">"Изображения"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index faaa943bb..f0bbc594c 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Mediji"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokalna pohrana"</string>
<string name="app_label" msgid="9035307001052716210">"Medijska pohrana"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Medij"</string>
<string name="artist_label" msgid="8105600993099120273">"Umjetnik"</string>
<string name="unknown" msgid="2059049215682829375">"Nepoznato"</string>
<string name="root_images" msgid="5861633549189045666">"Slike"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 87fa8d57f..b4f9df885 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Multimèdia"</string>
<string name="storage_description" msgid="4081716890357580107">"Emmagatzematge local"</string>
<string name="app_label" msgid="9035307001052716210">"Emmagatzematge multimèdia"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Contingut multimèdia"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Desconegut"</string>
<string name="root_images" msgid="5861633549189045666">"Imatges"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index b7a7b01a5..a95d75ab7 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Média"</string>
<string name="storage_description" msgid="4081716890357580107">"Místní úložiště"</string>
<string name="app_label" msgid="9035307001052716210">"Úložiště médií"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Média"</string>
<string name="artist_label" msgid="8105600993099120273">"Interpret"</string>
<string name="unknown" msgid="2059049215682829375">"Neznámý"</string>
<string name="root_images" msgid="5861633549189045666">"Obrázky"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 09cb22489..b605b8eda 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Medier"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokalt lager"</string>
<string name="app_label" msgid="9035307001052716210">"Medielagring"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Mediefiler"</string>
<string name="artist_label" msgid="8105600993099120273">"Kunstner"</string>
<string name="unknown" msgid="2059049215682829375">"Ukendt"</string>
<string name="root_images" msgid="5861633549189045666">"Billeder"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 6d329cdb8..592438e60 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Medien"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokaler Speicher"</string>
<string name="app_label" msgid="9035307001052716210">"Medienspeicher"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Medien"</string>
<string name="artist_label" msgid="8105600993099120273">"Interpret"</string>
<string name="unknown" msgid="2059049215682829375">"Unbekannt"</string>
<string name="root_images" msgid="5861633549189045666">"Bilder"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 1f6e00b5b..7dd01b958 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Multimedia"</string>
<string name="storage_description" msgid="4081716890357580107">"Almacenamiento local"</string>
<string name="app_label" msgid="9035307001052716210">"Almacenamiento multimedia"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Multimedia"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Desconocido"</string>
<string name="root_images" msgid="5861633549189045666">"Imágenes"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 4bc45cb91..05d275a7f 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Multimedia"</string>
<string name="storage_description" msgid="4081716890357580107">"Almacenamiento local"</string>
<string name="app_label" msgid="9035307001052716210">"Almacenamiento multimedia"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Multimedia"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Desconocido"</string>
<string name="root_images" msgid="5861633549189045666">"Imágenes"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index e23f14a54..7c89a9c60 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Meedia"</string>
<string name="storage_description" msgid="4081716890357580107">"Kohalik salvestusruum"</string>
<string name="app_label" msgid="9035307001052716210">"Meediumi salvestusruum"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Meedia"</string>
<string name="artist_label" msgid="8105600993099120273">"Esitaja"</string>
<string name="unknown" msgid="2059049215682829375">"Teadmata"</string>
<string name="root_images" msgid="5861633549189045666">"Pildid"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 0297200af..d50fce988 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"رسانه"</string>
<string name="storage_description" msgid="4081716890357580107">"فضای ذخیره‌سازی محلی"</string>
<string name="app_label" msgid="9035307001052716210">"فضای ذخیره‌سازی رسانه"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"رسانه"</string>
<string name="artist_label" msgid="8105600993099120273">"هنرمند"</string>
<string name="unknown" msgid="2059049215682829375">"نامشخص"</string>
<string name="root_images" msgid="5861633549189045666">"تصویر"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 01ae3320f..5975059b8 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Paikallinen tallennustila"</string>
<string name="app_label" msgid="9035307001052716210">"Median tallennustila"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Media"</string>
<string name="artist_label" msgid="8105600993099120273">"Artisti"</string>
<string name="unknown" msgid="2059049215682829375">"Tuntematon"</string>
<string name="root_images" msgid="5861633549189045666">"Kuvat"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index a16db0242..d4f4dbf25 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Multimédia"</string>
<string name="storage_description" msgid="4081716890357580107">"Stockage local"</string>
<string name="app_label" msgid="9035307001052716210">"Stockage multimédia"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Contenu multimédia"</string>
<string name="artist_label" msgid="8105600993099120273">"Artiste"</string>
<string name="unknown" msgid="2059049215682829375">"Inconnu"</string>
<string name="root_images" msgid="5861633549189045666">"Images"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index dd7186978..488291d66 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Multimédia"</string>
<string name="storage_description" msgid="4081716890357580107">"Stockage local"</string>
<string name="app_label" msgid="9035307001052716210">"Stockage multimédia"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Multimédia"</string>
<string name="artist_label" msgid="8105600993099120273">"Artiste"</string>
<string name="unknown" msgid="2059049215682829375">"Inconnu"</string>
<string name="root_images" msgid="5861633549189045666">"Images"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index f7e788b4d..7a3965b33 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Multimedia"</string>
<string name="storage_description" msgid="4081716890357580107">"Almacenamento local"</string>
<string name="app_label" msgid="9035307001052716210">"Almacenamento multimedia"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Contido multimedia"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Descoñecida"</string>
<string name="root_images" msgid="5861633549189045666">"Imaxes"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index d228a361a..6edca727e 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"મીડિયા"</string>
<string name="storage_description" msgid="4081716890357580107">"સ્થાનિક સ્ટોરેજ"</string>
<string name="app_label" msgid="9035307001052716210">"મીડિયા સ્ટોરેજ"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"મીડિયા"</string>
<string name="artist_label" msgid="8105600993099120273">"કલાકાર"</string>
<string name="unknown" msgid="2059049215682829375">"અજાણ"</string>
<string name="root_images" msgid="5861633549189045666">"છબીઓ"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index ec5eb9ce6..25d7c373f 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"मीडिया"</string>
<string name="storage_description" msgid="4081716890357580107">"स्थानीय जगह"</string>
<string name="app_label" msgid="9035307001052716210">"मीडिया मेमोरी"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"मीडिया"</string>
<string name="artist_label" msgid="8105600993099120273">"कलाकार"</string>
<string name="unknown" msgid="2059049215682829375">"अज्ञात"</string>
<string name="root_images" msgid="5861633549189045666">"इमेज"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 5f3a31fd5..ed77a5531 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Mediji"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokalna pohrana"</string>
<string name="app_label" msgid="9035307001052716210">"Pohranjivanje na mediju"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Mediji"</string>
<string name="artist_label" msgid="8105600993099120273">"Izvođač"</string>
<string name="unknown" msgid="2059049215682829375">"Nepoznato"</string>
<string name="root_images" msgid="5861633549189045666">"Slike"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index a6ba73c69..03b231e54 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Média"</string>
<string name="storage_description" msgid="4081716890357580107">"Helyi tárhely"</string>
<string name="app_label" msgid="9035307001052716210">"Médiatároló"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Média"</string>
<string name="artist_label" msgid="8105600993099120273">"Előadó"</string>
<string name="unknown" msgid="2059049215682829375">"Ismeretlen"</string>
<string name="root_images" msgid="5861633549189045666">"Képek"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 83add6b07..129fd1c15 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Մեդիա"</string>
<string name="storage_description" msgid="4081716890357580107">"Սարքի հիշողություն"</string>
<string name="app_label" msgid="9035307001052716210">"Մեդիա կրիչ"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Մեդիա"</string>
<string name="artist_label" msgid="8105600993099120273">"Կատարող"</string>
<string name="unknown" msgid="2059049215682829375">"Անհայտ"</string>
<string name="root_images" msgid="5861633549189045666">"Պատկերներ"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 2cbaae1b1..d7a135b88 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Penyimpanan lokal"</string>
<string name="app_label" msgid="9035307001052716210">"Penyimpanan Media"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Media"</string>
<string name="artist_label" msgid="8105600993099120273">"Artis"</string>
<string name="unknown" msgid="2059049215682829375">"Tidak diketahui"</string>
<string name="root_images" msgid="5861633549189045666">"Gambar"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 0d9e5ca26..7b874ded6 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Margmiðlun"</string>
<string name="storage_description" msgid="4081716890357580107">"Staðbundin vistun"</string>
<string name="app_label" msgid="9035307001052716210">"Efnisgeymsla"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Efni"</string>
<string name="artist_label" msgid="8105600993099120273">"Flytjandi"</string>
<string name="unknown" msgid="2059049215682829375">"Óþekkt"</string>
<string name="root_images" msgid="5861633549189045666">"Myndir"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 19a189e26..e13675116 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Supporti multimediali"</string>
<string name="storage_description" msgid="4081716890357580107">"Archiviazione locale"</string>
<string name="app_label" msgid="9035307001052716210">"Media Storage"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Contenuti multimediali"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Sconosciuto"</string>
<string name="root_images" msgid="5861633549189045666">"Immagini"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 2d2f56ba7..2b3c0c177 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"מדיה"</string>
<string name="storage_description" msgid="4081716890357580107">"אחסון מקומי"</string>
<string name="app_label" msgid="9035307001052716210">"אחסון מדיה"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"מדיה"</string>
<string name="artist_label" msgid="8105600993099120273">"אומן"</string>
<string name="unknown" msgid="2059049215682829375">"לא ידוע"</string>
<string name="root_images" msgid="5861633549189045666">"תמונות"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 752b73088..f9b15517d 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Мультимeдиа"</string>
<string name="storage_description" msgid="4081716890357580107">"Жергілікті жад"</string>
<string name="app_label" msgid="9035307001052716210">"Мультимедиа қоймасы"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Meдиа"</string>
<string name="artist_label" msgid="8105600993099120273">"Орындаушы"</string>
<string name="unknown" msgid="2059049215682829375">"Белгісіз"</string>
<string name="root_images" msgid="5861633549189045666">"Кескіндер"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index ff82a6bbe..cc694f1bd 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"មេឌៀ"</string>
<string name="storage_description" msgid="4081716890357580107">"ទំហំផ្ទុកមូលដ្ឋាន"</string>
<string name="app_label" msgid="9035307001052716210">"ទំហំផ្ទុកមេឌៀ"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"មេឌៀ"</string>
<string name="artist_label" msgid="8105600993099120273">"សិល្បករ"</string>
<string name="unknown" msgid="2059049215682829375">"មិនស្គាល់"</string>
<string name="root_images" msgid="5861633549189045666">"រូបភាព"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index cf5d34f5a..5b11b4cde 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"미디어"</string>
<string name="storage_description" msgid="4081716890357580107">"로컬 저장소"</string>
<string name="app_label" msgid="9035307001052716210">"미디어 저장소"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"미디어"</string>
<string name="artist_label" msgid="8105600993099120273">"아티스트"</string>
<string name="unknown" msgid="2059049215682829375">"알 수 없음"</string>
<string name="root_images" msgid="5861633549189045666">"이미지"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 0153ba110..241291929 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"ມີເດຍ"</string>
<string name="storage_description" msgid="4081716890357580107">"ບ່ອນຈັດເກັບຂໍ້ມູນໃນເຄື່ອງ"</string>
<string name="app_label" msgid="9035307001052716210">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນມີເດຍ"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"ສື່"</string>
<string name="artist_label" msgid="8105600993099120273">"ສິນລະປິນ"</string>
<string name="unknown" msgid="2059049215682829375">"ບໍ່ຮູ້ຈັກ"</string>
<string name="root_images" msgid="5861633549189045666">"ຮູບພາບ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 106a7567c..5e7b7d53c 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Medija"</string>
<string name="storage_description" msgid="4081716890357580107">"Vietinė saugykla"</string>
<string name="app_label" msgid="9035307001052716210">"Medijos saugykla"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Medija"</string>
<string name="artist_label" msgid="8105600993099120273">"Atlikėjas"</string>
<string name="unknown" msgid="2059049215682829375">"Nežinoma"</string>
<string name="root_images" msgid="5861633549189045666">"Vaizdai"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index fb3996cda..a201f12f1 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Multivide"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokālā krātuve"</string>
<string name="app_label" msgid="9035307001052716210">"Multivides krātuve"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Multivide"</string>
<string name="artist_label" msgid="8105600993099120273">"Izpildītājs"</string>
<string name="unknown" msgid="2059049215682829375">"Nezināms"</string>
<string name="root_images" msgid="5861633549189045666">"Attēli"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 048adefd8..bb9f21ad1 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Storan setempat"</string>
<string name="app_label" msgid="9035307001052716210">"Storan Media"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Media"</string>
<string name="artist_label" msgid="8105600993099120273">"Artis"</string>
<string name="unknown" msgid="2059049215682829375">"Tidak diketahui"</string>
<string name="root_images" msgid="5861633549189045666">"Imej"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 3ab35be3c..5d33238e5 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"မီဒီယာ"</string>
<string name="storage_description" msgid="4081716890357580107">"စက်တွင်း သိုလှောင်ခန်း"</string>
<string name="app_label" msgid="9035307001052716210">"မီဒီယာ သိုလှောင်ခန်း"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"မီဒီယာ"</string>
<string name="artist_label" msgid="8105600993099120273">"အနုပညာရှင်"</string>
<string name="unknown" msgid="2059049215682829375">"အမျိုးအမည်မသိ"</string>
<string name="root_images" msgid="5861633549189045666">"ပုံများ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 7a82cf185..e73aea8a7 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Medier"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokal lagring"</string>
<string name="app_label" msgid="9035307001052716210">"Medielagring"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Medier"</string>
<string name="artist_label" msgid="8105600993099120273">"Artist"</string>
<string name="unknown" msgid="2059049215682829375">"Ukjent"</string>
<string name="root_images" msgid="5861633549189045666">"Bilder"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index f622d145c..f1ea0631e 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"मिडिया"</string>
<string name="storage_description" msgid="4081716890357580107">"स्थानीय भण्डारण"</string>
<string name="app_label" msgid="9035307001052716210">"मिडिया भण्डारण"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"मिडिया"</string>
<string name="artist_label" msgid="8105600993099120273">"कलाकार"</string>
<string name="unknown" msgid="2059049215682829375">"अज्ञात"</string>
<string name="root_images" msgid="5861633549189045666">"फोटो"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index f4281ef88..832d8bc5c 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokale opslag"</string>
<string name="app_label" msgid="9035307001052716210">"Mediaopslag"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Media"</string>
<string name="artist_label" msgid="8105600993099120273">"Artiest"</string>
<string name="unknown" msgid="2059049215682829375">"Onbekend"</string>
<string name="root_images" msgid="5861633549189045666">"Afbeeldingen"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index d4a3ef927..05511dadd 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"ମିଡିଆ"</string>
<string name="storage_description" msgid="4081716890357580107">"ଲୋକାଲ୍‍ ଷ୍ଟୋରେଜ୍‍"</string>
<string name="app_label" msgid="9035307001052716210">"ମିଡିଆ ଷ୍ଟୋରେଜ୍"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"ମିଡିଆ"</string>
<string name="artist_label" msgid="8105600993099120273">"କଳାକାର"</string>
<string name="unknown" msgid="2059049215682829375">"ଅଜଣା"</string>
<string name="root_images" msgid="5861633549189045666">"ଇମେଜ୍‌"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 74221a293..b1c7c3535 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"ਮੀਡੀਆ"</string>
<string name="storage_description" msgid="4081716890357580107">"ਸਥਾਨਕ ਸਟੋਰੇਜ"</string>
<string name="app_label" msgid="9035307001052716210">"ਮੀਡੀਆ ਸਟੋਰੇਜ"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"ਮੀਡੀਆ"</string>
<string name="artist_label" msgid="8105600993099120273">"ਕਲਾਕਾਰ"</string>
<string name="unknown" msgid="2059049215682829375">"ਅਗਿਆਤ"</string>
<string name="root_images" msgid="5861633549189045666">"ਚਿੱਤਰ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index c02b334f9..e40e6d4f0 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Multimedia"</string>
<string name="storage_description" msgid="4081716890357580107">"Pamięć lokalna"</string>
<string name="app_label" msgid="9035307001052716210">"Przechowywanie multimediów"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Multimedia"</string>
<string name="artist_label" msgid="8105600993099120273">"Wykonawca"</string>
<string name="unknown" msgid="2059049215682829375">"Nieznany"</string>
<string name="root_images" msgid="5861633549189045666">"Obrazy"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 1ef42106e..4db74cf64 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Multimédia"</string>
<string name="storage_description" msgid="4081716890357580107">"Armazenamento local"</string>
<string name="app_label" msgid="9035307001052716210">"Armazenamento de multimédia"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Multimédia"</string>
<string name="artist_label" msgid="8105600993099120273">"Artista"</string>
<string name="unknown" msgid="2059049215682829375">"Desconhecido"</string>
<string name="root_images" msgid="5861633549189045666">"Imagens"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index fa3d679e6..4844c6434 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Мультимедиа"</string>
<string name="storage_description" msgid="4081716890357580107">"Локальное хранилище"</string>
<string name="app_label" msgid="9035307001052716210">"Хранилище мультимедиа"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Мультимедиа"</string>
<string name="artist_label" msgid="8105600993099120273">"Исполнитель"</string>
<string name="unknown" msgid="2059049215682829375">"Неизвестно"</string>
<string name="root_images" msgid="5861633549189045666">"Изображения"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index f25fdc012..c9ffffc3e 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"මාධ්‍ය"</string>
<string name="storage_description" msgid="4081716890357580107">"පෙදෙසි ආචයනය"</string>
<string name="app_label" msgid="9035307001052716210">"මාධ්‍ය ගබඩාව"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"මාධ්‍ය"</string>
<string name="artist_label" msgid="8105600993099120273">"කලාකරු"</string>
<string name="unknown" msgid="2059049215682829375">"නොදනී"</string>
<string name="root_images" msgid="5861633549189045666">"රූප"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 48d0663c8..a3653acd1 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Predstavnost"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokalna shramba"</string>
<string name="app_label" msgid="9035307001052716210">"Shramba za predstavnost"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Predstavnost"</string>
<string name="artist_label" msgid="8105600993099120273">"Izvajalec"</string>
<string name="unknown" msgid="2059049215682829375">"Neznano"</string>
<string name="root_images" msgid="5861633549189045666">"Slike"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index f77ad0657..35f975fbe 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Hapësira ruajtëse lokale"</string>
<string name="app_label" msgid="9035307001052716210">"Hapësira ruajtëse e medias"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Media"</string>
<string name="artist_label" msgid="8105600993099120273">"Artisti"</string>
<string name="unknown" msgid="2059049215682829375">"I panjohur"</string>
<string name="root_images" msgid="5861633549189045666">"Fotografitë"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index ac14f37dc..262b83a38 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokal lagring"</string>
<string name="app_label" msgid="9035307001052716210">"Medialagring"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Media"</string>
<string name="artist_label" msgid="8105600993099120273">"Artist"</string>
<string name="unknown" msgid="2059049215682829375">"Okänd"</string>
<string name="root_images" msgid="5861633549189045666">"Bilder"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 74302d3dc..735c55a46 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Maudhui"</string>
<string name="storage_description" msgid="4081716890357580107">"Hifadhi ya ndani"</string>
<string name="app_label" msgid="9035307001052716210">"Hifadhi ya Maudhui"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Maudhui"</string>
<string name="artist_label" msgid="8105600993099120273">"Msanii"</string>
<string name="unknown" msgid="2059049215682829375">"Isiyojulikana"</string>
<string name="root_images" msgid="5861633549189045666">"Picha"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 33cd6e42f..468586c6b 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"மீடியா"</string>
<string name="storage_description" msgid="4081716890357580107">"சாதனச் சேமிப்பகம்"</string>
<string name="app_label" msgid="9035307001052716210">"மீடியா சேமிப்பிடம்"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"மீடியா"</string>
<string name="artist_label" msgid="8105600993099120273">"கலைஞர்"</string>
<string name="unknown" msgid="2059049215682829375">"அறியாதது"</string>
<string name="root_images" msgid="5861633549189045666">"Images"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 6f44520e8..a70fab96f 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"మీడియా"</string>
<string name="storage_description" msgid="4081716890357580107">"స్థానిక నిల్వ"</string>
<string name="app_label" msgid="9035307001052716210">"మీడియా నిల్వ"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"మీడియా"</string>
<string name="artist_label" msgid="8105600993099120273">"కళాకారుడు"</string>
<string name="unknown" msgid="2059049215682829375">"తెలియదు"</string>
<string name="root_images" msgid="5861633549189045666">"ఇమేజ్‌లు"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 8ab39b74a..b666c7c39 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"สื่อ"</string>
<string name="storage_description" msgid="4081716890357580107">"พื้นที่เก็บข้อมูลในเครื่อง"</string>
<string name="app_label" msgid="9035307001052716210">"พื้นที่เก็บข้อมูลสื่อ"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"สื่อ"</string>
<string name="artist_label" msgid="8105600993099120273">"ศิลปิน"</string>
<string name="unknown" msgid="2059049215682829375">"ไม่ทราบ"</string>
<string name="root_images" msgid="5861633549189045666">"รูปภาพ"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index b252453c9..ac006cc17 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Media"</string>
<string name="storage_description" msgid="4081716890357580107">"Lokal na storage"</string>
<string name="app_label" msgid="9035307001052716210">"Storage ng Media"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Media"</string>
<string name="artist_label" msgid="8105600993099120273">"Artist"</string>
<string name="unknown" msgid="2059049215682829375">"Hindi alam"</string>
<string name="root_images" msgid="5861633549189045666">"Mga Larawan"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 5232087e1..b03bb2986 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Medya"</string>
<string name="storage_description" msgid="4081716890357580107">"Yerel depolama"</string>
<string name="app_label" msgid="9035307001052716210">"Medya Deposu"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Medya"</string>
<string name="artist_label" msgid="8105600993099120273">"Sanatçı"</string>
<string name="unknown" msgid="2059049215682829375">"Bilinmiyor"</string>
<string name="root_images" msgid="5861633549189045666">"Resimler"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index b19305f14..39ff52577 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Медіа-файли"</string>
<string name="storage_description" msgid="4081716890357580107">"Локальна пам’ять"</string>
<string name="app_label" msgid="9035307001052716210">"Сховище медіа-файлів"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Медіа"</string>
<string name="artist_label" msgid="8105600993099120273">"Виконавець"</string>
<string name="unknown" msgid="2059049215682829375">"Невідомо"</string>
<string name="root_images" msgid="5861633549189045666">"Зображення"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index c72614a2c..ae76c01a7 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"میڈیا"</string>
<string name="storage_description" msgid="4081716890357580107">"مقامی اسٹوریج"</string>
<string name="app_label" msgid="9035307001052716210">"میڈیا اسٹوریج"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"میڈیا"</string>
<string name="artist_label" msgid="8105600993099120273">"فنکار"</string>
<string name="unknown" msgid="2059049215682829375">"نامعلوم"</string>
<string name="root_images" msgid="5861633549189045666">"تصاوير"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 5881452ad..b256d480a 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"媒体"</string>
<string name="storage_description" msgid="4081716890357580107">"本地存储空间"</string>
<string name="app_label" msgid="9035307001052716210">"媒体存储设备"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"媒体"</string>
<string name="artist_label" msgid="8105600993099120273">"音乐人"</string>
<string name="unknown" msgid="2059049215682829375">"未知"</string>
<string name="root_images" msgid="5861633549189045666">"图片"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 2012981e3..cbc74c5ec 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"媒體"</string>
<string name="storage_description" msgid="4081716890357580107">"本機儲存空間"</string>
<string name="app_label" msgid="9035307001052716210">"媒體儲存空間"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"媒體"</string>
<string name="artist_label" msgid="8105600993099120273">"歌手"</string>
<string name="unknown" msgid="2059049215682829375">"不明"</string>
<string name="root_images" msgid="5861633549189045666">"相片"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 8a2c73dd0..a1e6f7872 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"媒體"</string>
<string name="storage_description" msgid="4081716890357580107">"本機儲存空間"</string>
<string name="app_label" msgid="9035307001052716210">"媒體儲存空間"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"媒體"</string>
<string name="artist_label" msgid="8105600993099120273">"演出者"</string>
<string name="unknown" msgid="2059049215682829375">"不明"</string>
<string name="root_images" msgid="5861633549189045666">"圖片"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 08b6cf687..1fd002956 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -19,8 +19,7 @@
<string name="uid_label" msgid="8421971615411294156">"Abezind"</string>
<string name="storage_description" msgid="4081716890357580107">"Isitoreji sasendaweni"</string>
<string name="app_label" msgid="9035307001052716210">"Isitoreji Semidiya"</string>
- <!-- no translation found for picker_app_label (4254039089502164761) -->
- <skip />
+ <string name="picker_app_label" msgid="4254039089502164761">"Imidiya"</string>
<string name="artist_label" msgid="8105600993099120273">"Umculi"</string>
<string name="unknown" msgid="2059049215682829375">"Akwaziwa"</string>
<string name="root_images" msgid="5861633549189045666">"Izithombe"</string>
diff --git a/res/values/drawables.xml b/res/values/drawables.xml
deleted file mode 100644
index a5a0b3d3b..000000000
--- a/res/values/drawables.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
- ~ Copyright (C) 2022 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>
- <!-- Override StyledPlayerView default control buttons -->
- <drawable name="exo_styled_controls_play">@drawable/ic_preview_play</drawable>
- <drawable name="exo_styled_controls_pause">@drawable/ic_preview_pause</drawable>
-
-</resources> \ No newline at end of file
diff --git a/src/com/android/providers/media/DatabaseHelper.java b/src/com/android/providers/media/DatabaseHelper.java
index a5c25110e..e4700b074 100644
--- a/src/com/android/providers/media/DatabaseHelper.java
+++ b/src/com/android/providers/media/DatabaseHelper.java
@@ -87,6 +87,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -499,30 +500,29 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
@Override
public void onDowngrade(final SQLiteDatabase db, final int oldV, final int newV) {
- Log.w(TAG,
- String.format(
- "onDowngrade() for %s from %s to %s. Deleting database:%s in case of a "
- + "downgrade.",
- mName, oldV, newV, mName));
+ Log.w(TAG, String.format(Locale.ROOT,
+ "onDowngrade() for %s from %s to %s. Deleting database:%s in case of a "
+ + "downgrade.", mName, oldV, newV, mName));
deleteDatabaseFiles();
throw new IllegalStateException(
- String.format("Crashing MP process on database downgrade of %s.", mName));
+ String.format(Locale.ROOT, "Crashing MP process on database downgrade of %s.",
+ mName));
}
private void deleteDatabaseFiles() {
File dbDir = mContext.getDatabasePath(mName).getParentFile();
File[] files = dbDir.listFiles();
if (files == null) {
- Log.w(TAG,
- String.format("No database files found on path:%s.", dbDir.getAbsolutePath()));
+ Log.w(TAG, String.format(Locale.ROOT, "No database files found on path:%s.",
+ dbDir.getAbsolutePath()));
return;
}
for (File file : files) {
if (file.getName().startsWith(mName)) {
file.delete();
- Log.w(TAG,
- String.format("Database file:%s deleted.", file.getAbsolutePath()));
+ Log.w(TAG, String.format(Locale.ROOT, "Database file:%s deleted.",
+ file.getAbsolutePath()));
}
}
}
@@ -547,17 +547,17 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
boolean isLastUsedDatabaseSession = isLastUsedDatabaseSession(db);
Optional<Long> nextRowIdFromXattrOptional = getNextRowIdFromXattr();
if (isLastUsedDatabaseSession && nextRowIdFromXattrOptional.isPresent()) {
- Log.i(TAG, String.format("No database change across sequential open calls for %s.",
- mName));
+ Log.i(TAG, String.format(Locale.ROOT,
+ "No database change across sequential open calls for %s.", mName));
mNextRowIdBackup.set(nextRowIdFromXattrOptional.get());
updateSessionIdInDatabaseAndExternalStorage(db);
return;
}
- Log.w(TAG, String.format(
+ Log.w(TAG, String.format(Locale.ROOT,
"%s database inconsistent: isLastUsedDatabaseSession:%b, "
- + "nextRowIdOptionalPresent:%b",
- mName, isLastUsedDatabaseSession, nextRowIdFromXattrOptional.isPresent()));
+ + "nextRowIdOptionalPresent:%b", mName, isLastUsedDatabaseSession,
+ nextRowIdFromXattrOptional.isPresent()));
// TODO(b/222313219): Add an assert to ensure that next row id xattr is always
// present when DB session id matches across sequential open calls.
updateNextRowIdInDatabaseAndExternalStorage(db);
@@ -585,8 +585,8 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
boolean setOnExternalStorage = setXattr(DATA_MEDIA_XATTR_DIRECTORY_PATH,
getSessionIdXattrKeyForDatabase(), uuid);
if (setOnDatabase && setOnExternalStorage) {
- Log.i(TAG, String.format("SessionId set to %s on paths %s and %s.", uuid, db.getPath(),
- DATA_MEDIA_XATTR_DIRECTORY_PATH));
+ Log.i(TAG, String.format(Locale.ROOT, "SessionId set to %s on paths %s and %s.", uuid,
+ db.getPath(), DATA_MEDIA_XATTR_DIRECTORY_PATH));
}
}
@@ -1809,7 +1809,8 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
}
private void updateUserId(SQLiteDatabase db) {
- db.execSQL(String.format("ALTER TABLE files ADD COLUMN _user_id INTEGER DEFAULT %d;",
+ db.execSQL(String.format(Locale.ROOT,
+ "ALTER TABLE files ADD COLUMN _user_id INTEGER DEFAULT %d;",
UserHandle.myUserId()));
}
@@ -2271,10 +2272,10 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
backupNextRowId(nextRowId);
// Insert and delete a row to update sqlite_sequence counter
- db.execSQL(String.format("INSERT INTO files(_ID) VALUES (%d)", nextRowId));
- db.execSQL(String.format("DELETE FROM files WHERE _ID=%d", nextRowId));
- Log.i(TAG, String.format("Updated sqlite counter of Files table of %s to %d.", mName,
- nextRowId));
+ db.execSQL(String.format(Locale.ROOT, "INSERT INTO files(_ID) VALUES (%d)", nextRowId));
+ db.execSQL(String.format(Locale.ROOT, "DELETE FROM files WHERE _ID=%d", nextRowId));
+ Log.i(TAG, String.format(Locale.ROOT, "Updated sqlite counter of Files table of %s to %d.",
+ mName, nextRowId));
}
/**
@@ -2288,8 +2289,8 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
String.valueOf(backupId));
if (setOnExternalStorage) {
mNextRowIdBackup.set(backupId);
- Log.i(TAG, String.format("Backed up next row id as:%d on path:%s for %s.", backupId,
- DATA_MEDIA_XATTR_DIRECTORY_PATH, mName));
+ Log.i(TAG, String.format(Locale.ROOT, "Backed up next row id as:%d on path:%s for %s.",
+ backupId, DATA_MEDIA_XATTR_DIRECTORY_PATH, mName));
}
}
@@ -2299,7 +2300,7 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
Os.getxattr(DATA_MEDIA_XATTR_DIRECTORY_PATH,
getNextRowIdXattrKeyForDatabase()))));
} catch (Exception e) {
- Log.e(TAG, String.format("Xattr:%s not found on external storage.",
+ Log.e(TAG, String.format(Locale.ROOT, "Xattr:%s not found on external storage.",
getNextRowIdXattrKeyForDatabase()), e);
return Optional.empty();
}
@@ -2312,7 +2313,8 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
return EXTERNAL_DB_NEXT_ROW_ID_XATTR_KEY;
}
throw new RuntimeException(
- String.format("Next row id xattr key not defined for database:%s.", mName));
+ String.format(Locale.ROOT, "Next row id xattr key not defined for database:%s.",
+ mName));
}
protected String getSessionIdXattrKeyForDatabase() {
@@ -2322,7 +2324,8 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
return EXTERNAL_DB_SESSION_ID_XATTR_KEY;
}
throw new RuntimeException(
- String.format("Session id xattr key not defined for database:%s.", mName));
+ String.format(Locale.ROOT, "Session id xattr key not defined for database:%s.",
+ mName));
}
protected static boolean setXattr(String path, String key, String value) {
@@ -2334,9 +2337,8 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
Log.d(TAG, String.format("xattr set to %s for key:%s on path: %s.", value, key, path));
return true;
} catch (Exception e) {
- Log.e(TAG,
- String.format("Failed to set xattr:%s to %s for path: %s.", key, value, path),
- e);
+ Log.e(TAG, String.format(Locale.ROOT, "Failed to set xattr:%s to %s for path: %s.", key,
+ value, path), e);
return false;
}
}
@@ -2345,9 +2347,8 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
try {
return Optional.of(Arrays.toString(Os.getxattr(path, key)));
} catch (Exception e) {
- Log.w(TAG,
- String.format("Exception encountered while reading xattr:%s from path:%s.", key,
- path));
+ Log.w(TAG, String.format(Locale.ROOT,
+ "Exception encountered while reading xattr:%s from path:%s.", key, path));
return Optional.empty();
}
}
@@ -2380,7 +2381,8 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
}
if (!(new File(DATA_MEDIA_XATTR_DIRECTORY_PATH)).exists()) {
- Log.w(TAG, String.format("Skipping row id recovery as path:%s does not exist.",
+ Log.w(TAG, String.format(Locale.ROOT,
+ "Skipping row id recovery as path:%s does not exist.",
DATA_MEDIA_XATTR_DIRECTORY_PATH));
return false;
}
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 3305fa626..6354dcbbc 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -35,6 +35,7 @@ import static android.provider.MediaStore.MATCH_DEFAULT;
import static android.provider.MediaStore.MATCH_EXCLUDE;
import static android.provider.MediaStore.MATCH_INCLUDE;
import static android.provider.MediaStore.MATCH_ONLY;
+import static android.provider.MediaStore.MEDIA_IGNORE_FILENAME;
import static android.provider.MediaStore.MY_UID;
import static android.provider.MediaStore.PER_USER_RANGE;
import static android.provider.MediaStore.QUERY_ARG_DEFER_SCAN;
@@ -121,6 +122,7 @@ import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipDescription;
+import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
@@ -225,6 +227,7 @@ import com.android.providers.media.dao.FileRow;
import com.android.providers.media.fuse.ExternalStorageServiceImpl;
import com.android.providers.media.fuse.FuseDaemon;
import com.android.providers.media.metrics.PulledMetrics;
+import com.android.providers.media.photopicker.PhotoPickerActivity;
import com.android.providers.media.photopicker.PickerDataLayer;
import com.android.providers.media.photopicker.PickerSyncController;
import com.android.providers.media.photopicker.data.ExternalDbFacade;
@@ -342,6 +345,8 @@ public class MediaProvider extends ContentProvider {
private static final String DIRECTORY_MEDIA = "media";
private static final String DIRECTORY_THUMBNAILS = ".thumbnails";
+ private static final String TAKE_OVER_GET_CONTENT = "take_over_get_content";
+
/**
* Hard-coded filename where the current value of
* {@link DatabaseHelper#getOrCreateUuid} is persisted on a physical SD card
@@ -848,15 +853,16 @@ public class MediaProvider extends ContentProvider {
Optional<Long> nextRowIdBackupOptional = helper.getNextRowId();
if (!nextRowIdBackupOptional.isPresent()) {
- throw new RuntimeException(String.format("Cannot find next row id xattr for %s.",
- helper.getDatabaseName()));
+ throw new RuntimeException(
+ String.format(Locale.ROOT, "Cannot find next row id xattr for %s.",
+ helper.getDatabaseName()));
}
if (id >= nextRowIdBackupOptional.get()) {
helper.backupNextRowId(id);
} else {
- Log.v(TAG, String.format("Inserted id:%d less than next row id backup:%d.", id,
- nextRowIdBackupOptional.get()));
+ Log.v(TAG, String.format(Locale.ROOT, "Inserted id:%d less than next row id backup:%d.",
+ id, nextRowIdBackupOptional.get()));
}
}
@@ -1180,10 +1186,29 @@ public class MediaProvider extends ContentProvider {
mExternalStorageAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
}
+ checkDeviceConfigAndUpdateGetContentAlias();
+
PulledMetrics.initialize(context);
return true;
}
+ @VisibleForTesting
+ protected void checkDeviceConfigAndUpdateGetContentAlias() {
+ final String photoPickerGetContentActivity =
+ PhotoPickerActivity.class.getPackage().getName() + ".PhotoPickerGetContentActivity";
+ final ComponentName componentName = new ComponentName(getContext().getPackageName(),
+ photoPickerGetContentActivity);
+
+ final int expectedState = getBooleanDeviceConfig(TAKE_OVER_GET_CONTENT, false)
+ ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+
+ Log.i(TAG, "Change component enabled state to " + expectedState
+ + " for PhotoPickerGetContentActivity. ");
+ getContext().getPackageManager().setComponentEnabledSetting(componentName, expectedState,
+ PackageManager.DONT_KILL_APP);
+ }
+
Optional<DatabaseHelper> getDatabaseHelper(String dbName) {
if (dbName.equalsIgnoreCase(INTERNAL_DATABASE_NAME)) {
return Optional.of(mInternalDatabase);
@@ -1289,7 +1314,8 @@ public class MediaProvider extends ContentProvider {
cleanMediaFilesForRemovedUser(signal);
// Populate _SPECIAL_FORMAT column for files which have column value as NULL
- detectSpecialFormat(signal);
+ // TODO(b/236620024): Do not update generation_modified for special_format value update
+ // detectSpecialFormat(signal);
final long durationMillis = (SystemClock.elapsedRealtime() - startTime);
Metrics.logIdleMaintenance(MediaStore.VOLUME_EXTERNAL, itemCount,
@@ -6652,6 +6678,7 @@ public class MediaProvider extends ContentProvider {
final File[] files = thumbDir.listFiles();
for (File thumbFile : (files != null) ? files : new File[0]) {
if (Objects.equals(thumbFile.getName(), FILE_DATABASE_UUID)) continue;
+ if (Objects.equals(thumbFile.getName(), MEDIA_IGNORE_FILENAME)) continue;
final String name = FileUtils.extractFileName(thumbFile.getName());
try {
final long id = Long.parseLong(name);
@@ -7794,8 +7821,6 @@ public class MediaProvider extends ContentProvider {
}
private boolean isPickerUri(Uri uri) {
- // TODO(b/188394433): move this method to PickerResolver in the spirit of not
- // adding picker logic to MediaProvider
final int match = matchUri(uri, /* allowHidden */ isCallingPackageAllowedHidden());
return match == PICKER_ID;
}
@@ -8407,7 +8432,7 @@ public class MediaProvider extends ContentProvider {
private void deleteIfAllowed(Uri uri, Bundle extras, String path) {
try {
- final File file = new File(path);
+ final File file = new File(path).getCanonicalFile();
checkAccess(uri, extras, file, true);
deleteAndInvalidate(file);
} catch (Exception e) {
diff --git a/src/com/android/providers/media/photopicker/PhotoPickerActivity.java b/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
index aa89047e7..36e6fb771 100644
--- a/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
+++ b/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
@@ -35,6 +35,7 @@ import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.UserHandle;
@@ -139,7 +140,7 @@ public class PhotoPickerActivity extends AppCompatActivity {
try {
mPickerViewModel.parseValuesFromIntent(intent);
} catch (IllegalArgumentException e) {
- Log.e(TAG, "Finished activity due to an exception while parsing extras", e);
+ Log.e(TAG, "Finish activity due to an exception while parsing extras", e);
setCancelledResultAndFinishSelf();
}
@@ -238,6 +239,7 @@ public class PhotoPickerActivity extends AppCompatActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.browse) {
+ mPickerViewModel.logBrowseToDocumentsUi(Binder.getCallingUid(), getCallingPackage());
launchDocumentsUiAndFinishPicker();
}
return super.onOptionsItemSelected(item);
@@ -253,8 +255,8 @@ public class PhotoPickerActivity extends AppCompatActivity {
// GET_CONTENT for all (media and non-media) files opens DocumentsUi, but it still shows
// "Photo Picker app option. When the user clicks on "Photo Picker", the same intent which
// includes filters to show non-media files as well is forwarded to PhotoPicker.
- // Make sure Photo Picker is opened when the intent is explicitly forwarded.
- if (isIntentForwarded(intent)) {
+ // Make sure Photo Picker is opened when the intent is explicitly forwarded by documentsUi
+ if (isIntentReferredByDocumentsUi(getReferrer())) {
Log.i(TAG, "Open PhotoPicker when a forwarded ACTION_GET_CONTENT intent is received");
return;
}
@@ -264,8 +266,11 @@ public class PhotoPickerActivity extends AppCompatActivity {
}
}
- private static boolean isIntentForwarded(Intent intent) {
- return (intent.getFlags() & Intent.FLAG_ACTIVITY_FORWARD_RESULT) > 0;
+ private boolean isIntentReferredByDocumentsUi(Uri referrerAppUri) {
+ ComponentName documentsUiComponentName = getDocumentsUiComponentName(this);
+ String documentsUiPackageName = documentsUiComponentName != null
+ ? documentsUiComponentName.getPackageName() : null;
+ return referrerAppUri != null && referrerAppUri.getHost().equals(documentsUiPackageName);
}
private void launchDocumentsUiAndFinishPicker() {
diff --git a/src/com/android/providers/media/photopicker/PhotoPickerProvider.java b/src/com/android/providers/media/photopicker/PhotoPickerProvider.java
index 28a21f54a..79f7326e8 100644
--- a/src/com/android/providers/media/photopicker/PhotoPickerProvider.java
+++ b/src/com/android/providers/media/photopicker/PhotoPickerProvider.java
@@ -72,7 +72,7 @@ public class PhotoPickerProvider extends CloudMediaProvider {
CloudProviderQueryExtras.fromCloudMediaBundle(extras);
return mDbFacade.queryMedia(queryExtras.getGeneration(), queryExtras.getAlbumId(),
- queryExtras.getMimeType());
+ queryExtras.getMimeTypes());
}
@Override
@@ -88,7 +88,7 @@ public class PhotoPickerProvider extends CloudMediaProvider {
final CloudProviderQueryExtras queryExtras =
CloudProviderQueryExtras.fromCloudMediaBundle(extras);
- return mDbFacade.queryAlbums(queryExtras.getMimeType());
+ return mDbFacade.queryAlbums(queryExtras.getMimeTypes());
}
@Override
diff --git a/src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java b/src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java
index 0100c052c..d2cb83dcd 100644
--- a/src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java
+++ b/src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java
@@ -18,6 +18,7 @@ package com.android.providers.media.photopicker.data;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.BOOLEAN_DEFAULT;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.LIMIT_DEFAULT;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.LONG_DEFAULT;
+import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.STRING_ARRAY_DEFAULT;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.STRING_DEFAULT;
import android.os.Bundle;
@@ -31,7 +32,7 @@ import android.provider.MediaStore;
public class CloudProviderQueryExtras {
private final String mAlbumId;
private final String mAlbumAuthority;
- private final String mMimeType;
+ private final String[] mMimeTypes;
private final long mSizeBytes;
private final long mGeneration;
private final int mLimit;
@@ -41,7 +42,7 @@ public class CloudProviderQueryExtras {
private CloudProviderQueryExtras() {
mAlbumId = STRING_DEFAULT;
mAlbumAuthority = STRING_DEFAULT;
- mMimeType = STRING_DEFAULT;
+ mMimeTypes = STRING_ARRAY_DEFAULT;
mSizeBytes = LONG_DEFAULT;
mGeneration = LONG_DEFAULT;
mLimit = LIMIT_DEFAULT;
@@ -49,11 +50,11 @@ public class CloudProviderQueryExtras {
mIsVideo = BOOLEAN_DEFAULT;
}
- private CloudProviderQueryExtras (String albumId, String albumAuthority, String mimeType,
+ private CloudProviderQueryExtras(String albumId, String albumAuthority, String[] mimeTypes,
long sizeBytes, long generation, int limit, boolean isFavorite, boolean isVideo) {
mAlbumId = albumId;
mAlbumAuthority = albumAuthority;
- mMimeType = mimeType;
+ mMimeTypes = mimeTypes;
mSizeBytes = sizeBytes;
mGeneration = generation;
mLimit = limit;
@@ -70,7 +71,7 @@ public class CloudProviderQueryExtras {
final String albumId = bundle.getString(MediaStore.QUERY_ARG_ALBUM_ID, STRING_DEFAULT);
final String albumAuthority = bundle.getString(MediaStore.QUERY_ARG_ALBUM_AUTHORITY,
STRING_DEFAULT);
- final String mimeType = bundle.getString(MediaStore.QUERY_ARG_MIME_TYPE, STRING_DEFAULT);
+ final String[] mimeTypes = bundle.getStringArray(MediaStore.QUERY_ARG_MIME_TYPE);
final long sizeBytes = bundle.getLong(MediaStore.QUERY_ARG_SIZE_BYTES, LONG_DEFAULT);
final long generation = LONG_DEFAULT;
@@ -81,7 +82,7 @@ public class CloudProviderQueryExtras {
final boolean isVideo = localProvider.equals(albumAuthority)
&& AlbumColumns.ALBUM_ID_VIDEOS.equals(albumId);
- return new CloudProviderQueryExtras(albumId, albumAuthority, mimeType, sizeBytes,
+ return new CloudProviderQueryExtras(albumId, albumAuthority, mimeTypes, sizeBytes,
generation, limit, isFavorite, isVideo);
}
@@ -93,8 +94,8 @@ public class CloudProviderQueryExtras {
final String albumId = bundle.getString(CloudMediaProviderContract.EXTRA_ALBUM_ID,
STRING_DEFAULT);
final String albumAuthority = STRING_DEFAULT;
- final String mimeType = bundle.getString(CloudMediaProviderContract.EXTRA_MIME_TYPE,
- STRING_DEFAULT);
+ final String[] mimeTypes = bundle.getStringArray(
+ CloudMediaProviderContract.EXTRA_MIME_TYPE);
final long sizeBytes = bundle.getLong(CloudMediaProviderContract.EXTRA_SIZE_LIMIT_BYTES,
LONG_DEFAULT);
final long generation = bundle.getLong(CloudMediaProviderContract.EXTRA_SYNC_GENERATION,
@@ -104,14 +105,14 @@ public class CloudProviderQueryExtras {
final boolean isFavorite = BOOLEAN_DEFAULT;
final boolean isVideo = BOOLEAN_DEFAULT;
- return new CloudProviderQueryExtras(albumId, albumAuthority, mimeType, sizeBytes,
+ return new CloudProviderQueryExtras(albumId, albumAuthority, mimeTypes, sizeBytes,
generation, limit, isFavorite, isVideo);
}
public PickerDbFacade.QueryFilter toQueryFilter() {
PickerDbFacade.QueryFilterBuilder qfb = new PickerDbFacade.QueryFilterBuilder(mLimit);
qfb.setSizeBytes(mSizeBytes);
- qfb.setMimeType(mMimeType);
+ qfb.setMimeTypes(mMimeTypes);
qfb.setIsFavorite(mIsFavorite);
qfb.setIsVideo(mIsVideo);
qfb.setAlbumId(mAlbumId);
@@ -121,7 +122,7 @@ public class CloudProviderQueryExtras {
public Bundle toCloudMediaBundle() {
final Bundle extras = new Bundle();
extras.putString(CloudMediaProviderContract.EXTRA_ALBUM_ID, mAlbumId);
- extras.putString(CloudMediaProviderContract.EXTRA_MIME_TYPE, mMimeType);
+ extras.putStringArray(CloudMediaProviderContract.EXTRA_MIME_TYPE, mMimeTypes);
extras.putLong(CloudMediaProviderContract.EXTRA_SIZE_LIMIT_BYTES, mSizeBytes);
return extras;
@@ -135,8 +136,8 @@ public class CloudProviderQueryExtras {
return mAlbumAuthority;
}
- public String getMimeType() {
- return mMimeType;
+ public String[] getMimeTypes() {
+ return mMimeTypes;
}
public long getSizeBytes() {
diff --git a/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java b/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java
index d849c1f5d..98f0693e0 100644
--- a/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java
+++ b/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java
@@ -27,10 +27,10 @@ import static android.provider.CloudMediaProviderContract.EXTRA_SYNC_GENERATION;
import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.LONG_DEFAULT;
+import static com.android.providers.media.photopicker.data.PickerDbFacade.addMimeTypesToQueryBuilderAndSelectionArgs;
import static com.android.providers.media.photopicker.util.CursorUtils.getCursorLong;
import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
import static com.android.providers.media.util.DatabaseUtils.bindList;
-import static com.android.providers.media.util.DatabaseUtils.replaceMatchAnyChar;
import android.content.ContentValues;
import android.content.Context;
@@ -272,7 +272,7 @@ public class ExternalDbFacade {
* Returns all items from the files table where {@link MediaColumns#GENERATION_MODIFIED}
* is greater than {@code generation}.
*/
- public Cursor queryMedia(long generation, String albumId, String mimeType) {
+ public Cursor queryMedia(long generation, String albumId, String[] mimeTypes) {
final List<String> selectionArgs = new ArrayList<>();
final String orderBy = CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MILLIS + " DESC";
@@ -281,7 +281,7 @@ public class ExternalDbFacade {
qb.appendWhereStandalone(WHERE_GREATER_GENERATION);
selectionArgs.add(String.valueOf(generation));
- selectionArgs.addAll(appendWhere(qb, albumId, mimeType));
+ selectionArgs.addAll(appendWhere(qb, albumId, mimeTypes));
return qb.query(db, PROJECTION_MEDIA_COLUMNS, /* select */ null,
selectionArgs.toArray(new String[selectionArgs.size()]), /* groupBy */ null,
@@ -374,14 +374,14 @@ public class ExternalDbFacade {
* Categories are determined with the {@link #LOCAL_ALBUM_IDS}.
* If there are no media items under an albumId, the album is skipped from the results.
*/
- public Cursor queryAlbums(String mimeType) {
+ public Cursor queryAlbums(String[] mimeTypes) {
final MatrixCursor c = new MatrixCursor(AlbumColumns.ALL_PROJECTION);
for (String albumId: LOCAL_ALBUM_IDS) {
Cursor cursor = mDatabaseHelper.runWithTransaction(db -> {
final SQLiteQueryBuilder qb = createMediaQueryBuilder();
final List<String> selectionArgs = new ArrayList<>();
- selectionArgs.addAll(appendWhere(qb, albumId, mimeType));
+ selectionArgs.addAll(appendWhere(qb, albumId, mimeTypes));
return qb.query(db, PROJECTION_ALBUM_DB, /* selection */ null,
selectionArgs.toArray(new String[selectionArgs.size()]), /* groupBy */ null,
@@ -419,13 +419,10 @@ public class ExternalDbFacade {
}
private static List<String> appendWhere(SQLiteQueryBuilder qb, String albumId,
- String mimeType) {
+ String[] mimeTypes) {
final List<String> selectionArgs = new ArrayList<>();
- if (mimeType != null) {
- qb.appendWhereStandalone(WHERE_MIME_TYPE);
- selectionArgs.add(replaceMatchAnyChar(mimeType));
- }
+ addMimeTypesToQueryBuilderAndSelectionArgs(qb, selectionArgs, mimeTypes);
if (albumId == null) {
return selectionArgs;
diff --git a/src/com/android/providers/media/photopicker/data/ItemsProvider.java b/src/com/android/providers/media/photopicker/data/ItemsProvider.java
index fb3287286..07fed2694 100644
--- a/src/com/android/providers/media/photopicker/data/ItemsProvider.java
+++ b/src/com/android/providers/media/photopicker/data/ItemsProvider.java
@@ -16,8 +16,6 @@
package com.android.providers.media.photopicker.data;
-import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentProvider;
@@ -30,15 +28,12 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.CloudMediaProviderContract;
-import android.provider.CloudMediaProviderContract.AlbumColumns;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import com.android.modules.utils.build.SdkLevel;
import com.android.providers.media.PickerUriResolver;
-import com.android.providers.media.photopicker.data.PickerDbFacade;
import com.android.providers.media.photopicker.data.model.Category;
import com.android.providers.media.photopicker.data.model.UserId;
@@ -57,7 +52,7 @@ public class ItemsProvider {
/**
* Returns a {@link Cursor} to all images/videos based on the param passed for
- * {@code categoryType}, {@code offset}, {@code limit}, {@code mimeType} and {@code userId}.
+ * {@code categoryType}, {@code offset}, {@code limit}, {@code mimeTypes} and {@code userId}.
*
* <p>
* By default the returned {@link Cursor} sorts by latest date taken.
@@ -77,13 +72,13 @@ public class ItemsProvider {
*/
@Nullable
public Cursor getItems(Category category, int offset,
- int limit, @Nullable String mimeType, @Nullable UserId userId) throws
+ int limit, @Nullable String[] mimeTypes, @Nullable UserId userId) throws
IllegalArgumentException {
if (userId == null) {
userId = UserId.CURRENT_USER;
}
- return queryMedia(limit, mimeType, category, userId);
+ return queryMedia(limit, mimeTypes, category, userId);
}
/**
@@ -107,15 +102,15 @@ public class ItemsProvider {
* in the category,
*/
@Nullable
- public Cursor getCategories(@Nullable String mimeType, @Nullable UserId userId) {
+ public Cursor getCategories(@Nullable String[] mimeTypes, @Nullable UserId userId) {
if (userId == null) {
userId = UserId.CURRENT_USER;
}
- return queryAlbums(mimeType, userId);
+ return queryAlbums(mimeTypes, userId);
}
- private Cursor queryMedia(int limit, @Nullable String mimeType,
+ private Cursor queryMedia(int limit, String[] mimeTypes,
@NonNull Category category, @NonNull UserId userId)
throws IllegalStateException {
final Bundle extras = new Bundle();
@@ -127,7 +122,9 @@ public class ItemsProvider {
return null;
}
extras.putInt(MediaStore.QUERY_ARG_LIMIT, limit);
- extras.putString(MediaStore.QUERY_ARG_MIME_TYPE, mimeType);
+ if (mimeTypes != null) {
+ extras.putStringArray(MediaStore.QUERY_ARG_MIME_TYPE, mimeTypes);
+ }
extras.putString(MediaStore.QUERY_ARG_ALBUM_ID, category.getId());
extras.putString(MediaStore.QUERY_ARG_ALBUM_AUTHORITY, category.getAuthority());
@@ -144,7 +141,7 @@ public class ItemsProvider {
}
@Nullable
- private Cursor queryAlbums(@Nullable String mimeType, @NonNull UserId userId) {
+ private Cursor queryAlbums(@Nullable String[] mimeTypes, @NonNull UserId userId) {
final Bundle extras = new Bundle();
try (ContentProviderClient client = userId.getContentResolver(mContext)
.acquireUnstableContentProviderClient(MediaStore.AUTHORITY)) {
@@ -153,7 +150,9 @@ public class ItemsProvider {
+ MediaStore.AUTHORITY);
return null;
}
- extras.putString(MediaStore.QUERY_ARG_MIME_TYPE, mimeType);
+ if (mimeTypes != null) {
+ extras.putStringArray(MediaStore.QUERY_ARG_MIME_TYPE, mimeTypes);
+ }
final Uri uri = PickerUriResolver.PICKER_INTERNAL_URI.buildUpon()
.appendPath(PickerUriResolver.ALBUM_PATH).build();
diff --git a/src/com/android/providers/media/photopicker/data/PickerDbFacade.java b/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
index 3585a94e3..1a0dda2c1 100644
--- a/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
+++ b/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
@@ -545,12 +545,12 @@ public class PickerDbFacade {
private final long mId;
private final String mAlbumId;
private final long mSizeBytes;
- private final String mMimeType;
+ private final String[] mMimeTypes;
private final boolean mIsFavorite;
private final boolean mIsVideo;
private QueryFilter(int limit, long dateTakenBeforeMs, long dateTakenAfterMs, long id,
- String albumId, long sizeBytes, String mimeType, boolean isFavorite,
+ String albumId, long sizeBytes, String[] mimeTypes, boolean isFavorite,
boolean isVideo) {
this.mLimit = limit;
this.mDateTakenBeforeMs = dateTakenBeforeMs;
@@ -558,7 +558,7 @@ public class PickerDbFacade {
this.mId = id;
this.mAlbumId = albumId;
this.mSizeBytes = sizeBytes;
- this.mMimeType = mimeType;
+ this.mMimeTypes = mimeTypes;
this.mIsFavorite = isFavorite;
this.mIsVideo = isVideo;
}
@@ -568,6 +568,7 @@ public class PickerDbFacade {
public static class QueryFilterBuilder {
public static final long LONG_DEFAULT = -1;
public static final String STRING_DEFAULT = null;
+ public static final String[] STRING_ARRAY_DEFAULT = null;
public static final boolean BOOLEAN_DEFAULT = false;
public static final int LIMIT_DEFAULT = 1000;
@@ -578,7 +579,7 @@ public class PickerDbFacade {
private long id = LONG_DEFAULT;
private String albumId = STRING_DEFAULT;
private long sizeBytes = LONG_DEFAULT;
- private String mimeType = STRING_DEFAULT;
+ private String[] mimeTypes = STRING_ARRAY_DEFAULT;
private boolean isFavorite = BOOLEAN_DEFAULT;
private boolean mIsVideo = BOOLEAN_DEFAULT;
@@ -622,8 +623,8 @@ public class PickerDbFacade {
return this;
}
- public QueryFilterBuilder setMimeType(String mimeType) {
- this.mimeType = mimeType;
+ public QueryFilterBuilder setMimeTypes(String[] mimeTypes) {
+ this.mimeTypes = mimeTypes;
return this;
}
@@ -649,7 +650,7 @@ public class PickerDbFacade {
public QueryFilter build() {
return new QueryFilter(limit, dateTakenBeforeMs, dateTakenAfterMs, id, albumId,
- sizeBytes, mimeType, isFavorite, mIsVideo);
+ sizeBytes, mimeTypes, isFavorite, mIsVideo);
}
}
@@ -737,10 +738,7 @@ public class PickerDbFacade {
qb.appendWhereStandalone(WHERE_MIME_TYPE);
selectionArgs.add("video/%");
}
- if (query.mMimeType != null) {
- qb.appendWhereStandalone(WHERE_MIME_TYPE);
- selectionArgs.add(query.mMimeType.replace('*', '%'));
- }
+ addMimeTypesToQueryBuilderAndSelectionArgs(qb, selectionArgs, query.mMimeTypes);
Cursor cursor = qb.query(mDatabase, PROJECTION_ALBUM_DB, /* selection */ null,
selectionArgs.toArray(new String[0]), /* groupBy */ null, /* having */ null,
@@ -1004,10 +1002,8 @@ public class PickerDbFacade {
selectArgs.add(String.valueOf(query.mSizeBytes));
}
- if (query.mMimeType != null) {
- qb.appendWhereStandalone(WHERE_MIME_TYPE);
- selectArgs.add(replaceMatchAnyChar(query.mMimeType));
- }
+ addMimeTypesToQueryBuilderAndSelectionArgs(qb, selectArgs, query.mMimeTypes);
+
if (query.mIsVideo) {
qb.appendWhereStandalone(WHERE_MIME_TYPE);
selectArgs.add(VIDEO_MIME_TYPES);
@@ -1025,6 +1021,27 @@ public class PickerDbFacade {
return selectArgs.toArray(new String[selectArgs.size()]);
}
+ static void addMimeTypesToQueryBuilderAndSelectionArgs(SQLiteQueryBuilder qb,
+ List<String> selectionArgs, String[] mimeTypes) {
+ if (mimeTypes == null) {
+ return;
+ }
+
+ mimeTypes = replaceMatchAnyChar(mimeTypes);
+ ArrayList<String> whereMimeTypes = new ArrayList<>();
+ for (String mimeType : mimeTypes) {
+ if (!TextUtils.isEmpty(mimeType)) {
+ whereMimeTypes.add(WHERE_MIME_TYPE);
+ selectionArgs.add(mimeType);
+ }
+ }
+
+ if (whereMimeTypes.isEmpty()) {
+ return;
+ }
+ qb.appendWhereStandalone(TextUtils.join(" OR ", whereMimeTypes));
+ }
+
private static SQLiteQueryBuilder createMediaQueryBuilder() {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(TABLE_MEDIA);
diff --git a/src/com/android/providers/media/photopicker/metrics/PhotoPickerUiEventLogger.java b/src/com/android/providers/media/photopicker/metrics/PhotoPickerUiEventLogger.java
index 511c6b6c0..076ad318d 100644
--- a/src/com/android/providers/media/photopicker/metrics/PhotoPickerUiEventLogger.java
+++ b/src/com/android/providers/media/photopicker/metrics/PhotoPickerUiEventLogger.java
@@ -29,7 +29,9 @@ public class PhotoPickerUiEventLogger {
@UiEvent(doc = "Photo picker opened in work profile")
PHOTO_PICKER_OPEN_WORK_PROFILE(943),
@UiEvent(doc = "Photo picker opened via GET_CONTENT intent")
- PHOTO_PICKER_OPEN_GET_CONTENT(1080);
+ PHOTO_PICKER_OPEN_GET_CONTENT(1080),
+ @UiEvent(doc = "DocumentsUi opened by clicking on Browse in Photo picker")
+ PHOTO_PICKER_BROWSE_DOCUMENTSUI(1085);
private final int mId;
@@ -75,4 +77,17 @@ public class PhotoPickerUiEventLogger {
callingPackage,
instanceId);
}
+
+ /**
+ * Log metrics to notify that user has clicked on "Browse..." in Photo picker overflow menu.
+ * This UI click even opens DocumentsUi.
+ */
+ public void logBrowseToDocumentsUi(InstanceId instanceId, int callingUid,
+ String callingPackage) {
+ logger.logWithInstanceId(
+ PhotoPickerEvent.PHOTO_PICKER_BROWSE_DOCUMENTSUI,
+ callingUid,
+ callingPackage,
+ instanceId);
+ }
}
diff --git a/src/com/android/providers/media/photopicker/ui/AlbumsTabFragment.java b/src/com/android/providers/media/photopicker/ui/AlbumsTabFragment.java
index 3f946ca60..5060486a6 100644
--- a/src/com/android/providers/media/photopicker/ui/AlbumsTabFragment.java
+++ b/src/com/android/providers/media/photopicker/ui/AlbumsTabFragment.java
@@ -47,7 +47,7 @@ public class AlbumsTabFragment extends TabFragment {
setEmptyMessage(R.string.picker_albums_empty_message);
final AlbumsTabAdapter adapter = new AlbumsTabAdapter(mImageLoader, this::onItemClick,
- mPickerViewModel.hasMimeTypeFilter());
+ mPickerViewModel.hasMimeTypeFilters());
mPickerViewModel.getCategories().observe(this, categoryList -> {
adapter.updateCategoryList(categoryList);
// Handle emptyView's visibility
diff --git a/src/com/android/providers/media/photopicker/ui/ExoPlayerWrapper.java b/src/com/android/providers/media/photopicker/ui/ExoPlayerWrapper.java
deleted file mode 100644
index 44ceea275..000000000
--- a/src/com/android/providers/media/photopicker/ui/ExoPlayerWrapper.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2021 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.photopicker.ui;
-
-import android.content.Context;
-import android.media.AudioAttributes;
-import android.media.AudioFocusRequest;
-import android.media.AudioManager;
-import android.net.Uri;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-
-import com.android.providers.media.R;
-import com.android.providers.media.photopicker.data.MuteStatus;
-
-import com.google.android.exoplayer2.DefaultLoadControl;
-import com.google.android.exoplayer2.DefaultRenderersFactory;
-import com.google.android.exoplayer2.ExoPlayer;
-import com.google.android.exoplayer2.LoadControl;
-import com.google.android.exoplayer2.MediaItem;
-import com.google.android.exoplayer2.Player;
-import com.google.android.exoplayer2.analytics.DefaultAnalyticsCollector;
-import com.google.android.exoplayer2.source.MediaParserExtractorAdapter;
-import com.google.android.exoplayer2.source.ProgressiveMediaSource;
-import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
-import com.google.android.exoplayer2.ui.StyledPlayerView;
-import com.google.android.exoplayer2.upstream.ContentDataSource;
-import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
-import com.google.android.exoplayer2.util.Clock;
-
-/**
- * A helper class that assists in initialize/prepare/play/release of ExoPlayer. The class assumes
- * that all its public methods are called from main thread only.
- */
-class ExoPlayerWrapper {
- private static final String TAG = "ExoPlayerWrapper";
- // The minimum duration of media that the player will attempt to ensure is buffered at all
- // times.
- private static final int MIN_BUFFER_MS = 1000;
- // The maximum duration of media that the player will attempt to buffer.
- private static final int MAX_BUFFER_MS = 2000;
- // The duration of media that must be buffered for playback to start or resume following a user
- // action such as a seek.
- private static final int BUFFER_FOR_PLAYBACK_MS = 1000;
- // The default duration of media that must be buffered for playback to resume after a rebuffer.
- private static final int BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = 1000;
- private static final LoadControl sLoadControl = new DefaultLoadControl.Builder()
- .setBufferDurationsMs(
- MIN_BUFFER_MS,
- MAX_BUFFER_MS,
- BUFFER_FOR_PLAYBACK_MS,
- BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS).build();
- private static final long PLAYER_CONTROL_ON_PLAY_TIMEOUT_MS = 1000;
- private static final float VOLUME_LEVEL_MUTE = 0.0f;
- private static final AudioAttributes sAudioAttributes = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.USAGE_MEDIA)
- .setUsage(AudioAttributes.CONTENT_TYPE_MOVIE)
- .build();
-
- private final Context mContext;
- private final MuteStatus mMuteStatus;
- private ExoPlayer mExoPlayer;
- private boolean mIsPlayerReleased = true;
- private boolean mShouldShowControlsForNext = true;
- private boolean mIsAccessibilityEnabled = false;
- private AudioFocusRequest mAudioFocusRequest = null;
-
- public ExoPlayerWrapper(Context context, MuteStatus muteStatus) {
- mContext = context;
- mMuteStatus = muteStatus;
- }
-
- /**
- * Prepares the {@link ExoPlayer} and attaches it to given {@code styledPlayerView} and starts
- * playing.
- * Note: The method tries to release the {@link ExoPlayer} before preparing the new one. As we
- * don't have previous page's {@link StyledPlayerView}, we can't switch the player from previous
- * {@link StyledPlayerView} to new one. Hence, we try to create a new {@link ExoPlayer} instead.
- */
- public void prepareAndPlay(StyledPlayerView styledPlayerView, ImageView imageView, Uri uri) {
- // TODO(b/197083539): Explore options for not re-creating ExoPlayer everytime.
- initializeExoPlayer(uri);
-
- setupPlayerLayout(styledPlayerView, imageView);
-
- // Prepare the player and play the video
- mExoPlayer.prepare();
- mExoPlayer.setPlayWhenReady(true);
- mIsPlayerReleased = false;
- }
-
- public void resetPlayerIfNecessary() {
- // Clear state of the previous player controls visibility state. Controls visibility state
- // will only be tracked and used for contiguous videos in the preview.
- mShouldShowControlsForNext = true;
- // Release the player if necessary.
- releaseIfNecessary();
- }
-
- private void initializeExoPlayer(Uri uri) {
- // Try releasing the ExoPlayer first.
- releaseIfNecessary();
-
- mExoPlayer = createExoPlayer();
- // We always start from the beginning of the video, and we always repeat the video in a loop
- mExoPlayer.setRepeatMode(Player.REPEAT_MODE_ONE);
- // We only play one video in the player, hence we should always use setMediaItem instead of
- // ExoPlayer#addMediaItem
- mExoPlayer.setMediaItem(MediaItem.fromUri(uri));
- }
-
- private ExoPlayer createExoPlayer() {
- // ProgressiveMediaFactory will be enough for video playback of videos on the device.
- // This also reduces apk size.
- ProgressiveMediaSource.Factory mediaSourceFactory = new ProgressiveMediaSource.Factory(
- () -> new ContentDataSource(mContext), MediaParserExtractorAdapter.FACTORY);
-
- return new ExoPlayer.Builder(mContext,
- new DefaultRenderersFactory(mContext),
- mediaSourceFactory,
- new DefaultTrackSelector(mContext),
- sLoadControl,
- DefaultBandwidthMeter.getSingletonInstance(mContext),
- new DefaultAnalyticsCollector(Clock.DEFAULT))
- .setHandleAudioBecomingNoisy(true)
- .build();
- }
-
- private void setupPlayerLayout(StyledPlayerView styledPlayerView, ImageView imageView) {
- // Step1: Set-up Player layout
- // TODO(b/197083539): Remove this if it drains battery.
- styledPlayerView.setKeepScreenOn(true);
- styledPlayerView.setPlayer(mExoPlayer);
- styledPlayerView.setVisibility(View.VISIBLE);
-
- // Hide ImageView when the player is ready.
- mExoPlayer.addListener(new Player.Listener() {
- @Override
- public void onPlaybackStateChanged(int playbackState) {
- if (playbackState == Player.STATE_READY ) {
- imageView.setVisibility(View.GONE);
- }
- }
- });
-
- // Step2: Set-up player control view
- // Set-up video controls for accessibility mode
- // Set Accessibility listeners and update the video controller visibility accordingly
- AccessibilityManager accessibilityManager =
- mContext.getSystemService(AccessibilityManager.class);
- accessibilityManager.addAccessibilityStateChangeListener(
- enabled -> updateControllerForAccessibilty(enabled, styledPlayerView));
- updateControllerForAccessibilty(accessibilityManager.isEnabled(), styledPlayerView);
-
- // Set-up video controls for non-accessibility mode
- // Track if the controller layout should be visible for the next video.
- styledPlayerView.setControllerVisibilityListener(
- visibility -> mShouldShowControlsForNext = (visibility == View.VISIBLE));
- // Video controls will be visible if
- // 1. this is the first video preview page or
- // 2. the previous video had controls visible when the page was swiped or
- // 3. the previous page was not a video preview
- // or if we are in accessibility mode.
- if (mShouldShowControlsForNext) {
- styledPlayerView.showController();
- }
-
- // Player controls needs to be auto-hidden if they are shown
- // 1. when the video starts previewing or
- // 2. when the video starts playing from paused state.
- // To achieve this, we hide the controller whenever player state changes to 'play'
- mExoPlayer.addListener(new Player.Listener() {
- @Override
- public void onIsPlayingChanged(boolean isPlaying) {
- if (mIsAccessibilityEnabled) {
- // Player controls are always visible in accessibility mode.
- return;
- }
-
- // We don't have to hide controls if the state changed to PAUSED or controller
- // isn't visible.
- if (!isPlaying || !mShouldShowControlsForNext) return;
-
- // Set controller visibility of the next video to false so that we don't show the
- // controls on the next video.
- mShouldShowControlsForNext = false;
- // Auto hide controller after 1s of player state changing to "Play".
- styledPlayerView.postDelayed(() -> styledPlayerView.hideController(),
- PLAYER_CONTROL_ON_PLAY_TIMEOUT_MS);
- }
- });
-
- // Step3: Set-up mute button
- final ImageButton muteButton = styledPlayerView.findViewById(R.id.preview_mute);
- handleAudioFocusAndInitVolumeState(muteButton);
-
- // Add click listeners for mute button
- muteButton.setOnClickListener(v -> {
- mMuteStatus.setVolumeMuted(!mMuteStatus.isVolumeMuted());
- handleAudioFocusAndInitVolumeState(muteButton);
- });
-
- // Request or abandon audio focus on player state change.
- mExoPlayer.addListener(new Player.Listener() {
- @Override
- public void onIsPlayingChanged(boolean isPlaying) {
- if (isPlaying) {
- handleAudioFocusAndInitVolumeState(muteButton);
- } else {
- abandonAudioFocusIfAny();
- }
- }
- });
- }
-
- /**
- * Requests AudioFocus if current state of the volume state is volume on. Sets the volume of
- * the playback if the AudioFocus request is granted.
- * Also, updates the mute button based on the state of the muteStatus.
- */
- private void handleAudioFocusAndInitVolumeState(ImageButton muteButton) {
- if (mMuteStatus.isVolumeMuted()) {
- mExoPlayer.setVolume(VOLUME_LEVEL_MUTE);
- abandonAudioFocusIfAny();
- } else if (requestAudioFocus() == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- mExoPlayer.setVolume(getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC));
- }
-
- updateMuteButtonState(muteButton, mMuteStatus.isVolumeMuted());
- }
-
- /**
- * Abandons the AudioFocus request so that the previous focus owner can resume their playback
- */
- private void abandonAudioFocusIfAny() {
- if (mAudioFocusRequest == null) return;
-
- getAudioManager().abandonAudioFocusRequest(mAudioFocusRequest);
- mAudioFocusRequest = null;
- }
-
- private void updateControllerForAccessibilty(boolean isEnabled,
- StyledPlayerView styledPlayerView) {
- mIsAccessibilityEnabled = isEnabled;
- if (isEnabled) {
- styledPlayerView.showController();
- styledPlayerView.setControllerHideOnTouch(false);
- } else {
- styledPlayerView.setControllerHideOnTouch(true);
- }
- }
-
- private void updateMuteButtonState(ImageButton muteButton, boolean isVolumeMuted) {
- updateMuteButtonContentDescription(muteButton, isVolumeMuted);
- updateMuteButtonIcon(muteButton, isVolumeMuted);
- }
-
- private void updateMuteButtonContentDescription(ImageButton muteButton, boolean isVolumeMuted) {
- muteButton.setContentDescription(
- mContext.getString(
- isVolumeMuted ? R.string.picker_unmute_video : R.string.picker_mute_video));
- }
-
- private void updateMuteButtonIcon(ImageButton muteButton, boolean isVolumeMuted) {
- muteButton.setImageResource(
- isVolumeMuted ? R.drawable.ic_volume_off : R.drawable.ic_volume_up);
- }
-
- private AudioManager getAudioManager() {
- return mContext.getSystemService(AudioManager.class);
- }
-
- private int requestAudioFocus() {
- // Always request new AudioFocus
- abandonAudioFocusIfAny();
-
- mAudioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
- .setAudioAttributes(sAudioAttributes)
- .setWillPauseWhenDucked(true)
- .setAcceptsDelayedFocusGain(true)
- .setOnAudioFocusChangeListener(focusChange -> {
- if (focusChange == AudioManager.AUDIOFOCUS_LOSS
- || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
- || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
- mExoPlayer.setPlayWhenReady(false);
- }
- }).build();
-
- // We don't need to reset mAudioFocusRequest to null on failure of requestAudioFocus. This
- // is because we always reset the AudioFocus before requesting, reset mechanism will also
- // try to abandon AudioFocus if there is any.
- return getAudioManager().requestAudioFocus(mAudioFocusRequest);
- }
-
- private void releaseIfNecessary() {
- // Release the player only when it's not already released. ExoPlayer doesn't crash if we try
- // to release already released player, but ExoPlayer#release() may not be a no-op, hence we
- // call release() only when it's not already released.
- if (!mIsPlayerReleased) {
- mExoPlayer.release();
- abandonAudioFocusIfAny();
- mIsPlayerReleased = true;
- }
- }
-}
diff --git a/src/com/android/providers/media/photopicker/ui/ImageLoader.java b/src/com/android/providers/media/photopicker/ui/ImageLoader.java
index d62947127..69f5e3d03 100644
--- a/src/com/android/providers/media/photopicker/ui/ImageLoader.java
+++ b/src/com/android/providers/media/photopicker/ui/ImageLoader.java
@@ -17,6 +17,7 @@
package com.android.providers.media.photopicker.ui;
import android.content.Context;
+import android.graphics.Bitmap;
import android.graphics.ImageDecoder;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -26,12 +27,15 @@ import android.util.Log;
import android.widget.ImageView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.providers.media.photopicker.data.model.Category;
import com.android.providers.media.photopicker.data.model.Item;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.load.Option;
+import com.bumptech.glide.load.resource.gif.GifDrawable;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.signature.ObjectKey;
@@ -44,6 +48,8 @@ public class ImageLoader {
public static final Option<Boolean> THUMBNAIL_REQUEST =
Option.memory(CloudMediaProviderContract.EXTRA_MEDIASTORE_THUMB, false);
private static final String TAG = "ImageLoader";
+ private static final RequestOptions THUMBNAIL_OPTION =
+ RequestOptions.option(THUMBNAIL_REQUEST, /* enableThumbnail */ true);
private final Context mContext;
public ImageLoader(Context context) {
@@ -60,11 +66,8 @@ public class ImageLoader {
// Always show all thumbnails as bitmap images instead of drawables
// This is to ensure that we do not animate any thumbnail (for eg GIF)
// TODO(b/194285082): Use drawable instead of bitmap, as it saves memory.
- Glide.with(mContext)
- .asBitmap()
- .load(category.getCoverUri())
- .apply(RequestOptions.option(THUMBNAIL_REQUEST, true))
- .into(imageView);
+ loadWithGlide(getBitmapRequestBuilder(category.getCoverUri()), THUMBNAIL_OPTION,
+ /* signature */ null, imageView);
}
/**
@@ -74,16 +77,11 @@ public class ImageLoader {
* @param imageView the imageView shows the thumbnail
*/
public void loadPhotoThumbnail(@NonNull Item item, @NonNull ImageView imageView) {
- Uri uri = item.getContentUri();
// Always show all thumbnails as bitmap images instead of drawables
// This is to ensure that we do not animate any thumbnail (for eg GIF)
// TODO(b/194285082): Use drawable instead of bitmap, as it saves memory.
- Glide.with(mContext)
- .asBitmap()
- .load(uri)
- .signature(getGlideSignature(item, /* prefix */ ""))
- .apply(RequestOptions.option(THUMBNAIL_REQUEST, true))
- .into(imageView);
+ loadWithGlide(getBitmapRequestBuilder(item.getContentUri()), THUMBNAIL_OPTION,
+ getGlideSignature(item, /* prefix */ ""), imageView);
}
/**
@@ -94,11 +92,8 @@ public class ImageLoader {
*/
public void loadImagePreview(@NonNull Item item, @NonNull ImageView imageView) {
if (item.isGif()) {
- Glide.with(mContext)
- .asGif()
- .load(item.getContentUri())
- .signature(getGlideSignature(item, /* prefix */ ""))
- .into(imageView);
+ loadWithGlide(getGifRequestBuilder(item.getContentUri()), /* requestOptions */ null,
+ getGlideSignature(item, /* prefix */ ""), imageView);
return;
}
@@ -108,11 +103,8 @@ public class ImageLoader {
}
// Preview as bitmap image for all other image types
- Glide.with(mContext)
- .asBitmap()
- .load(item.getContentUri())
- .signature(getGlideSignature(item, /* prefix */ ""))
- .into(imageView);
+ loadWithGlide(getBitmapRequestBuilder(item.getContentUri()), /* requestOptions */ null,
+ getGlideSignature(item, /* prefix */ ""), imageView);
}
private void loadAnimatedWebpPreview(@NonNull Item item, @NonNull ImageView imageView) {
@@ -129,22 +121,16 @@ public class ImageLoader {
// If we failed to decode drawable for a source using ImageDecoder, then try using uri
// directly. Glide will show static image for an animated webp. That is okay as we tried our
// best to load animated webp but couldn't, and we anyway show the GIF badge in preview.
- Glide.with(mContext)
- .load(drawable == null ? uri : drawable)
- .signature(getGlideSignature(item, /* prefix */ ""))
- .into(imageView);
+ loadWithGlide(getDrawableRequestBuilder(drawable == null ? uri : drawable),
+ /* requestOptions */ null, getGlideSignature(item, /* prefix */ ""), imageView);
}
/**
* Loads the image from first frame of the given video item
*/
public void loadImageFromVideoForPreview(@NonNull Item item, @NonNull ImageView imageView) {
- Glide.with(mContext)
- .asBitmap()
- .load(item.getContentUri())
- .apply(new RequestOptions().frame(1000))
- .signature(getGlideSignature(item, "Preview"))
- .into(imageView);
+ loadWithGlide(getBitmapRequestBuilder(item.getContentUri()),
+ new RequestOptions().frame(1000), getGlideSignature(item, "Preview"), imageView);
}
private ObjectKey getGlideSignature(Item item, String prefix) {
@@ -153,4 +139,37 @@ public class ImageLoader {
MediaStore.getVersion(mContext) + prefix + item.getContentUri().toString() +
item.getGenerationModified());
}
+
+ private RequestBuilder<Bitmap> getBitmapRequestBuilder(Uri uri) {
+ return Glide.with(mContext)
+ .asBitmap()
+ .load(uri);
+ }
+
+ private RequestBuilder<GifDrawable> getGifRequestBuilder(Uri uri) {
+ return Glide.with(mContext)
+ .asGif()
+ .load(uri);
+ }
+
+ private RequestBuilder<Drawable> getDrawableRequestBuilder(Object model) {
+ return Glide.with(mContext)
+ .load(model);
+ }
+
+ private <T> void loadWithGlide(RequestBuilder<T> requestBuilder,
+ @Nullable RequestOptions requestOptions, @Nullable ObjectKey signature,
+ ImageView imageView) {
+ RequestBuilder<T> newRequestBuilder = requestBuilder.clone();
+
+ if (requestOptions != null) {
+ newRequestBuilder = newRequestBuilder.apply(requestOptions);
+ }
+
+ if (signature != null) {
+ newRequestBuilder = newRequestBuilder.signature(signature);
+ }
+
+ newRequestBuilder.into(imageView);
+ }
}
diff --git a/src/com/android/providers/media/photopicker/ui/PlaybackHandler.java b/src/com/android/providers/media/photopicker/ui/PlaybackHandler.java
deleted file mode 100644
index 7d31af9cc..000000000
--- a/src/com/android/providers/media/photopicker/ui/PlaybackHandler.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2021 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.photopicker.ui;
-
-import android.content.Context;
-import android.net.Uri;
-import android.os.Looper;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.providers.media.R;
-import com.android.providers.media.photopicker.data.MuteStatus;
-import com.android.providers.media.photopicker.data.model.Item;
-
-import com.google.android.exoplayer2.ui.StyledPlayerView;
-
-/**
- * A class to handle page selected state to initiate video playback or release video player
- * resources. All the public methods of this class must be called from main thread as ExoPlayer
- * should be prepared/released from main thread.
- */
-class PlaybackHandler {
- private static final String TAG = "PlaybackHandler";
- // Only main thread can call the methods in this class, hence we don't need to guard mVideoUri
- // with lock while reading or writing to it.
- private Uri mVideoUri = null;
- private final ExoPlayerWrapper mExoPlayerWrapper;
-
- PlaybackHandler(Context context, MuteStatus muteStatus) {
- mExoPlayerWrapper = new ExoPlayerWrapper(context, muteStatus);
- }
-
- /**
- * Handles video playback for the {@link ViewPager2} page when it's selected i.e., completely
- * visible.
- * <ul>
- * <li> If the selected page is a video page, prepare and play the video associated with
- * selected page
- * <li> If the selected page is a video page and the same video is already playing, then no
- * action will be taken.
- * <li> If the selected page is non-video page, try releasing the ExoPlayer associated with
- * previous page that was selected.
- * </ul>
- * @param view {@link RecyclerView.ViewHolder#itemView} of the selected page.
- */
- public void handleVideoPlayback(View view) {
- assertMainThread();
-
- final Object tag = view.getTag();
- if (!(tag instanceof Item)) {
- throw new IllegalStateException("Expected Item tag to be set to " + view);
- }
-
- final Item item = (Item) tag;
- if (!item.isVideo()) {
- // We only need to handle video playback. For everything else, try releasing ExoPlayer
- // if there is a prepared ExoPlayer of the previous page, also reset any player states
- // when necessary.
- mExoPlayerWrapper.resetPlayerIfNecessary();
- mVideoUri = null;
- return;
- }
-
- final Uri videoUri = item.getContentUri();
- if (mVideoUri != null && mVideoUri.equals(videoUri)) {
- // Selected video is already handled. This must be a slight drag and drop, and we don't
- // have to change state of the player.
- Log.d(TAG, "Ignoring handlePageSelected of already selected page, with uri "
- + videoUri);
- return;
- }
-
- final StyledPlayerView styledPlayerView = view.findViewById(R.id.preview_player_view);
- if (styledPlayerView == null) {
- throw new IllegalStateException("Expected to find StyledPlayerView in " + view);
- }
- final ImageView imageView = view.findViewById(R.id.preview_video_image);
-
- mVideoUri = videoUri;
- mExoPlayerWrapper.prepareAndPlay(styledPlayerView, imageView, mVideoUri);
- }
-
- public void onViewAttachedToWindow(View itemView) {
- final ImageView imageView = itemView.findViewById(R.id.preview_video_image);
- imageView.setVisibility(View.VISIBLE);
- final StyledPlayerView styledPlayerView = itemView.findViewById(R.id.preview_player_view);
- styledPlayerView.setVisibility(View.GONE);
- styledPlayerView.setControllerVisibilityListener(null);
- styledPlayerView.hideController();
- }
-
- /**
- * Releases ExoPlayer if there is any. Also resets the saved video uri.
- */
- public void releaseResources() {
- assertMainThread();
-
- mVideoUri = null;
- mExoPlayerWrapper.resetPlayerIfNecessary();
- }
-
- private void assertMainThread() {
- if (Looper.getMainLooper().isCurrentThread()) return;
-
- throw new IllegalStateException("PlaybackHandler methods are expected to be called from"
- + " main thread. Current thread " + Looper.myLooper().getThread()
- + ", Main thread" + Looper.getMainLooper().getThread());
- }
-}
diff --git a/src/com/android/providers/media/photopicker/ui/PreviewAdapter.java b/src/com/android/providers/media/photopicker/ui/PreviewAdapter.java
index 8ce73117e..1df48777f 100644
--- a/src/com/android/providers/media/photopicker/ui/PreviewAdapter.java
+++ b/src/com/android/providers/media/photopicker/ui/PreviewAdapter.java
@@ -41,13 +41,10 @@ class PreviewAdapter extends RecyclerView.Adapter<BaseViewHolder> {
private List<Item> mItemList = new ArrayList<>();
private final ImageLoader mImageLoader;
private final RemotePreviewHandler mRemotePreviewHandler;
- private final PlaybackHandler mPlaybackHandler;
- private final boolean mIsRemotePreviewEnabled = RemotePreviewHandler.isRemotePreviewEnabled();
PreviewAdapter(Context context, MuteStatus muteStatus) {
mImageLoader = new ImageLoader(context);
mRemotePreviewHandler = new RemotePreviewHandler(context, muteStatus);
- mPlaybackHandler = new PlaybackHandler(context, muteStatus);
}
@NonNull
@@ -55,10 +52,8 @@ class PreviewAdapter extends RecyclerView.Adapter<BaseViewHolder> {
public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
if (viewType == ITEM_TYPE_IMAGE) {
return new PreviewImageHolder(viewGroup.getContext(), viewGroup, mImageLoader);
- } else {
- return new PreviewVideoHolder(viewGroup.getContext(), viewGroup, mImageLoader,
- mIsRemotePreviewEnabled);
}
+ return new PreviewVideoHolder(viewGroup.getContext(), viewGroup, mImageLoader);
}
@Override
@@ -76,17 +71,8 @@ class PreviewAdapter extends RecyclerView.Adapter<BaseViewHolder> {
final Item item = (Item) holder.itemView.getTag();
if (item.isVideo()) {
- // TODO(b/222506900): Refactor thumbnail show / hide logic to be handled from a single
- // place. Currently, we show the thumbnail here and hide it when playback starts in
- // PlaybackHandler/RemotePreviewHandler.
PreviewVideoHolder videoHolder = (PreviewVideoHolder) holder;
-
- if (mIsRemotePreviewEnabled) {
- mRemotePreviewHandler.onViewAttachedToWindow(videoHolder, item);
- return;
- }
-
- mPlaybackHandler.onViewAttachedToWindow(holder.itemView);
+ mRemotePreviewHandler.onViewAttachedToWindow(videoHolder, item);
}
}
@@ -106,28 +92,16 @@ class PreviewAdapter extends RecyclerView.Adapter<BaseViewHolder> {
}
void onHandlePageSelected(View itemView) {
- if (mIsRemotePreviewEnabled) {
- final Item item = (Item) itemView.getTag();
- mRemotePreviewHandler.onHandlePageSelected(item);
- return;
- }
-
- mPlaybackHandler.handleVideoPlayback(itemView);
+ final Item item = (Item) itemView.getTag();
+ mRemotePreviewHandler.onHandlePageSelected(item);
}
void onStop() {
- if (mIsRemotePreviewEnabled) {
- mRemotePreviewHandler.onStop();
- return;
- }
-
- mPlaybackHandler.releaseResources();
+ mRemotePreviewHandler.onStop();
}
void onDestroy() {
- if (mIsRemotePreviewEnabled) {
- mRemotePreviewHandler.onDestroy();
- }
+ mRemotePreviewHandler.onDestroy();
}
Item getItem(int position) {
diff --git a/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java b/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java
index dcb169655..e8287670c 100644
--- a/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java
+++ b/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java
@@ -45,25 +45,17 @@ public class PreviewVideoHolder extends BaseViewHolder {
private final ImageButton mPlayPauseButton;
private final ImageButton mMuteButton;
- PreviewVideoHolder(Context context, ViewGroup parent, ImageLoader imageLoader,
- boolean enabledCloudMediaPreview) {
- super(context, parent, enabledCloudMediaPreview ? R.layout.item_cloud_video_preview
- : R.layout.item_video_preview);
+ PreviewVideoHolder(Context context, ViewGroup parent, ImageLoader imageLoader) {
+ super(context, parent, R.layout.item_video_preview);
mImageLoader = imageLoader;
mImageView = itemView.findViewById(R.id.preview_video_image);
- mSurfaceView = enabledCloudMediaPreview ? itemView.findViewById(R.id.preview_player_view)
- : null;
- mPlayerFrame = enabledCloudMediaPreview ?
- itemView.findViewById(R.id.preview_player_frame) : null;
- mPlayerContainer = enabledCloudMediaPreview ?
- itemView.findViewById(R.id.preview_player_container) : null;
- mPlayerControlsRoot = enabledCloudMediaPreview ? itemView.findViewById(
- R.id.preview_player_controls) : null;
- mPlayPauseButton = enabledCloudMediaPreview ? itemView.findViewById(
- R.id.exo_play_pause) : null;
- mMuteButton = enabledCloudMediaPreview ? itemView.findViewById(
- R.id.preview_mute) : null;
+ mSurfaceView = itemView.findViewById(R.id.preview_player_view);
+ mPlayerFrame = itemView.findViewById(R.id.preview_player_frame);
+ mPlayerContainer = itemView.findViewById(R.id.preview_player_container);
+ mPlayerControlsRoot = itemView.findViewById(R.id.preview_player_controls);
+ mPlayPauseButton = itemView.findViewById(R.id.exo_play_pause);
+ mMuteButton = itemView.findViewById(R.id.preview_mute);
}
@Override
diff --git a/src/com/android/providers/media/photopicker/util/MimeFilterUtils.java b/src/com/android/providers/media/photopicker/util/MimeFilterUtils.java
index b028a00a2..7872343a4 100644
--- a/src/com/android/providers/media/photopicker/util/MimeFilterUtils.java
+++ b/src/com/android/providers/media/photopicker/util/MimeFilterUtils.java
@@ -57,24 +57,31 @@ public class MimeFilterUtils {
/**
* Extracts relevant mime type filter for the given intent
*/
- public static String getMimeTypeFilter(Intent intent) {
+ public static String[] getMimeTypeFilters(Intent intent) throws IllegalArgumentException {
// EXTRA_MIME_TYPES has higher priority over getType() filter.
if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
final String[] extraMimeTypes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
- if (extraMimeTypes.length == 1) {
- // We only support 1 mime type filter
- // TODO(b/224756380): Add support for multiple mime type filters
- return extraMimeTypes[0];
+ if (requiresUnsupportedFilters(extraMimeTypes)) {
+ if (Intent.ACTION_GET_CONTENT.equals(intent.getAction())) {
+ // This is a special case in which PhotoPicker is explicitly opened from
+ // DocumentsUI as it is seen as one of the options. In this show all images
+ // and videos.
+ // If this was not a special case, then the picker would close itself and
+ // redirect the request to DocumentsUI before hitting this point.
+ return null;
+ }
+
+ throw new IllegalArgumentException("Invalid EXTRA_MIME_TYPES value, only media "
+ + "mime type filters are accepted");
}
- // Show all images/videos for multiple or empty mime type filters
- return null;
+ return extraMimeTypes;
}
final String mimeType = intent.getType();
if (MimeFilterUtils.isMimeTypeMedia(mimeType)) {
- return mimeType;
+ return new String[] { mimeType };
}
return null;
@@ -86,13 +93,8 @@ public class MimeFilterUtils {
return true;
}
- // TODO(b/224756380): Add support for multiple mime type filters
- if (mimeTypeFilters.length > 1) {
- return true;
- }
-
for (String mimeTypeFilter : mimeTypeFilters) {
- if (!MimeFilterUtils.isMimeTypeMedia(mimeTypeFilter)) {
+ if (!isMimeTypeMedia(mimeTypeFilter)) {
return true;
}
}
diff --git a/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java b/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
index 00d8fd073..5fe5effc5 100644
--- a/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
+++ b/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
@@ -76,7 +76,7 @@ public class PickerViewModel extends AndroidViewModel {
private InstanceId mInstanceId;
private PhotoPickerUiEventLogger mLogger;
- private String mMimeTypeFilter = null;
+ private String[] mMimeTypeFilters = null;
private int mBottomSheetState;
private Category mCurrentCategory;
@@ -151,7 +151,7 @@ public class PickerViewModel extends AndroidViewModel {
final List<Item> items = new ArrayList<>();
try (Cursor cursor = mItemsProvider.getItems(category, /* offset */ 0,
- /* limit */ -1, mMimeTypeFilter, userId)) {
+ /* limit */ -1, mMimeTypeFilters, userId)) {
if (cursor == null || cursor.getCount() == 0) {
Log.d(TAG, "Didn't receive any items for " + category
+ ", either cursor is null or cursor count is zero");
@@ -268,7 +268,7 @@ public class PickerViewModel extends AndroidViewModel {
private List<Category> loadCategories(UserId userId) {
final List<Category> categoryList = new ArrayList<>();
- try (final Cursor cursor = mItemsProvider.getCategories(mMimeTypeFilter, userId)) {
+ try (final Cursor cursor = mItemsProvider.getCategories(mMimeTypeFilters, userId)) {
if (cursor == null || cursor.getCount() == 0) {
Log.d(TAG, "Didn't receive any categories, either cursor is null or"
+ " cursor count is zero");
@@ -304,10 +304,10 @@ public class PickerViewModel extends AndroidViewModel {
}
/**
- * Return whether the {@link #mMimeTypeFilter} is {@code null} or not
+ * Return whether the {@link #mMimeTypeFilters} is {@code null} or not
*/
- public boolean hasMimeTypeFilter() {
- return !TextUtils.isEmpty(mMimeTypeFilter);
+ public boolean hasMimeTypeFilters() {
+ return mMimeTypeFilters != null && mMimeTypeFilters.length > 0;
}
/**
@@ -316,7 +316,7 @@ public class PickerViewModel extends AndroidViewModel {
public void parseValuesFromIntent(Intent intent) throws IllegalArgumentException {
mUserIdManager.setIntentAndCheckRestrictions(intent);
- mMimeTypeFilter = MimeFilterUtils.getMimeTypeFilter(intent);
+ mMimeTypeFilters = MimeFilterUtils.getMimeTypeFilters(intent);
mSelection.parseSelectionValuesFromIntent(intent);
}
@@ -350,6 +350,13 @@ public class PickerViewModel extends AndroidViewModel {
}
}
+ /**
+ * Log metrics to notify that the user has clicked Browse to open DocumentsUi
+ */
+ public void logBrowseToDocumentsUi(int callingUid, String callingPackage) {
+ mLogger.logBrowseToDocumentsUi(mInstanceId, callingUid, callingPackage);
+ }
+
public InstanceId getInstanceId() {
return mInstanceId;
}
diff --git a/src/com/android/providers/media/scan/ModernMediaScanner.java b/src/com/android/providers/media/scan/ModernMediaScanner.java
index 41f53d57a..58759e206 100644
--- a/src/com/android/providers/media/scan/ModernMediaScanner.java
+++ b/src/com/android/providers/media/scan/ModernMediaScanner.java
@@ -1510,9 +1510,13 @@ public class ModernMediaScanner implements MediaScanner {
@VisibleForTesting
static @NonNull Optional<Integer> parseOptionalOrientation(int orientation) {
switch (orientation) {
+ case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
case ExifInterface.ORIENTATION_NORMAL: return Optional.of(0);
+ case ExifInterface.ORIENTATION_TRANSPOSE:
case ExifInterface.ORIENTATION_ROTATE_90: return Optional.of(90);
+ case ExifInterface.ORIENTATION_FLIP_VERTICAL:
case ExifInterface.ORIENTATION_ROTATE_180: return Optional.of(180);
+ case ExifInterface.ORIENTATION_TRANSVERSE:
case ExifInterface.ORIENTATION_ROTATE_270: return Optional.of(270);
default: return Optional.empty();
}
diff --git a/src/com/android/providers/media/util/DatabaseUtils.java b/src/com/android/providers/media/util/DatabaseUtils.java
index 55efafc7f..cec6f27f1 100644
--- a/src/com/android/providers/media/util/DatabaseUtils.java
+++ b/src/com/android/providers/media/util/DatabaseUtils.java
@@ -535,8 +535,14 @@ public class DatabaseUtils {
return sb.toString();
}
- public static String replaceMatchAnyChar(@NonNull String arg) {
- return arg.replace('*', '%');
+ public static String[] replaceMatchAnyChar(@NonNull String[] arg) {
+ String[] result = arg.clone();
+ for (int i = 0; i < arg.length; i++) {
+ if (result[i] != null) {
+ result[i] = result[i].replace('*', '%');
+ }
+ }
+ return result;
}
public static boolean parseBoolean(@Nullable Object value, boolean def) {
diff --git a/src/com/android/providers/media/util/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java
index ee421c97b..de789388f 100644
--- a/src/com/android/providers/media/util/FileUtils.java
+++ b/src/com/android/providers/media/util/FileUtils.java
@@ -49,8 +49,8 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.os.UserHandle;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.provider.MediaStore;
@@ -973,12 +973,13 @@ public class FileUtils {
"(?i)^Android/(?:data|media|obb)/([^/]+)(/?.*)?");
/**
- * Regex that matches Android/obb or Android/data path.
+ * Regex that matches exactly Android/obb or Android/data or Android/obb/ or Android/data/
+ * suffix absolute file path.
*/
private static final Pattern PATTERN_DATA_OR_OBB_PATH = Pattern.compile(
"(?i)^/storage/[^/]+/(?:[0-9]+/)?"
+ PROP_CROSS_USER_ROOT_PATTERN
- + "Android/(?:data|obb)(?:/.*)?$");
+ + "Android/(?:data|obb)/?$");
/**
* Regex that matches Android/obb or Android/data relative path (as defined in
@@ -1399,9 +1400,18 @@ public class FileUtils {
resolvedDisplayName = displayName;
}
- final File filePath = buildPath(volumePath,
- values.getAsString(MediaColumns.RELATIVE_PATH), resolvedDisplayName);
- values.put(MediaColumns.DATA, filePath.getAbsolutePath());
+ String relativePath = values.getAsString(MediaColumns.RELATIVE_PATH);
+ if (relativePath == null) {
+ relativePath = "";
+ }
+ try {
+ final File filePath = buildPath(volumePath, relativePath, resolvedDisplayName);
+ values.put(MediaColumns.DATA, filePath.getCanonicalPath());
+ } catch (IOException e) {
+ throw new IllegalArgumentException(
+ String.format("Failure in conversion to canonical file path. Failure path: %s.",
+ relativePath.concat(resolvedDisplayName)), e);
+ }
}
public static void sanitizeValues(@NonNull ContentValues values,
@@ -1625,18 +1635,28 @@ public class FileUtils {
Log.i(TAG, "Clearing cache for all apps");
final File rootDataDir = buildPath(Environment.getExternalStorageDirectory(),
"Android", "data");
- for (File appDataDir : rootDataDir.listFiles()) {
- try {
- final File appCacheDir = new File(appDataDir, "cache");
- if (appCacheDir.isDirectory()) {
- FileUtils.deleteContents(appCacheDir);
+ File[] appDataDirs = rootDataDir.listFiles();
+ if (appDataDirs == null) {
+ // Couldn't delete any app cache dirs because the call to list files in root data dir
+ // failed (b/234521806). It is not clear why this call would fail because root data
+ // dir path should be well-formed.
+ Log.e(TAG, String.format("Couldn't delete any app cache dirs in root data dir %s !",
+ rootDataDir.getAbsolutePath()));
+ status = OsConstants.EIO;
+ } else {
+ for (File appDataDir : appDataDirs) {
+ try {
+ final File appCacheDir = new File(appDataDir, "cache");
+ if (appCacheDir.isDirectory()) {
+ FileUtils.deleteContents(appCacheDir);
+ }
+ } catch (Exception e) {
+ // We want to avoid crashing MediaProvider at all costs, so we handle all
+ // "generic" exceptions here, and just report to the caller that an IO exception
+ // has occurred. We still try to clear the rest of the directories.
+ Log.e(TAG, "Couldn't delete all app cache dirs!", e);
+ status = OsConstants.EIO;
}
- } catch (Exception e) {
- // We want to avoid crashing MediaProvider at all costs, so we handle all "generic"
- // exceptions here, and just report to the caller that an IO exception has occurred.
- // We still try to clear the rest of the directories.
- Log.e(TAG, "Couldn't delete all app cache dirs!", e);
- status = OsConstants.EIO;
}
}
return status;
diff --git a/tests/Android.bp b/tests/Android.bp
index 05aa55604..17f90dbf0 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -158,7 +158,7 @@ android_test {
"androidx.core_core",
"androidx.test.rules",
"guava",
- "mockito-target-extended-minus-junit4",
+ "mockito-target",
"modules-utils-build",
"truth-prebuilt",
"com.google.android.material_material",
@@ -177,12 +177,6 @@ android_test {
"exoplayer-mediaprovider-ui",
],
- // these are needed for Extended Mockito
- jni_libs: [
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- ],
-
certificate: "media",
aaptflags: ["--custom-package com.android.providers.media"],
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index fd9486312..15c2be4c4 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -9,10 +9,7 @@
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
- <!--android:debuggable and android:largeHeap attributes are needed for Extended Mockito-->
- <application android:label="MediaProvider Tests"
- android:debuggable="true"
- android:largeHeap="true">
+ <application android:label="MediaProvider Tests">
<uses-library android:name="android.test.runner" />
<activity android:name="com.android.providers.media.GetResultActivity" />
diff --git a/tests/src/com/android/providers/media/IdleServiceTest.java b/tests/src/com/android/providers/media/IdleServiceTest.java
index 80d2261fc..39f7e6e5d 100644
--- a/tests/src/com/android/providers/media/IdleServiceTest.java
+++ b/tests/src/com/android/providers/media/IdleServiceTest.java
@@ -121,6 +121,7 @@ public class IdleServiceTest {
final File b = touch(buildPath(dir, DIRECTORY_MOVIES, ".thumbnails", "7654321.jpg"));
final File c = touch(buildPath(dir, DIRECTORY_PICTURES, ".thumbnails", id + ".jpg"));
final File d = touch(buildPath(dir, DIRECTORY_PICTURES, ".thumbnails", "random.bin"));
+ final File e = touch(buildPath(dir, DIRECTORY_PICTURES, ".thumbnails", ".nomedia"));
// Idle maintenance pass should clean up unknown files
MediaStore.runIdleMaintenance(resolver);
@@ -128,6 +129,7 @@ public class IdleServiceTest {
assertFalse(exists(b));
assertTrue(exists(c));
assertFalse(exists(d));
+ assertTrue(exists(e));
// And change the UUID, which emulates ejecting and mounting a different
// storage device; all thumbnails should then be invalidated
@@ -136,12 +138,13 @@ public class IdleServiceTest {
delete(uuidFile);
touch(uuidFile);
- // Idle maintenance pass should clean up all files
+ // Idle maintenance pass should clean up all files except .nomedia file
MediaStore.runIdleMaintenance(resolver);
assertFalse(exists(a));
assertFalse(exists(b));
assertFalse(exists(c));
assertFalse(exists(d));
+ assertTrue(exists(e));
}
/**
diff --git a/tests/src/com/android/providers/media/MediaProviderTest.java b/tests/src/com/android/providers/media/MediaProviderTest.java
index a04f57b97..ff56ee894 100644
--- a/tests/src/com/android/providers/media/MediaProviderTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderTest.java
@@ -78,6 +78,7 @@ import com.android.providers.media.util.FileUtilsTest;
import com.android.providers.media.util.SQLiteQueryBuilder;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Ignore;
@@ -375,6 +376,42 @@ public class MediaProviderTest {
}
}
+ @Test
+ public void testInsertionWithInvalidFilePath_throwsIllegalArgumentException() {
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH, "Android/media/com.example");
+ values.put(MediaStore.Images.Media.DISPLAY_NAME,
+ "./../../../../../../../../../../../data/media/test.txt");
+
+ IllegalArgumentException illegalArgumentException = Assert.assertThrows(
+ IllegalArgumentException.class, () -> sIsolatedResolver.insert(
+ MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY),
+ values));
+
+ assertThat(illegalArgumentException).hasMessageThat().contains(
+ "Primary directory Android not allowed for content://media/external_primary/file;"
+ + " allowed directories are [Download, Documents]");
+ }
+
+ @Test
+ public void testUpdationWithInvalidFilePath_throwsIllegalArgumentException() {
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH, "Download");
+ values.put(MediaStore.Images.Media.DISPLAY_NAME, "test.txt");
+ Uri uri = sIsolatedResolver.insert(
+ MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY),
+ values);
+
+ final ContentValues newValues = new ContentValues();
+ newValues.put(MediaStore.MediaColumns.DATA, "/storage/emulated/0/../../../data/media/");
+ IllegalArgumentException illegalArgumentException = Assert.assertThrows(
+ IllegalArgumentException.class,
+ () -> sIsolatedResolver.update(uri, newValues, null));
+
+ assertThat(illegalArgumentException).hasMessageThat().contains(
+ "Requested path /data/media doesn't appear under [/storage/emulated/0]");
+ }
+
/**
* We already have solid coverage of this logic in
* {@code CtsProviderTestCases}, but the coverage system currently doesn't
@@ -642,6 +679,11 @@ public class MediaProviderTest {
public int getCallingPackageTargetSdkVersion() {
return Build.VERSION_CODES.Q;
}
+
+ @Override
+ protected void checkDeviceConfigAndUpdateGetContentAlias() {
+ // Ignore this as test app cannot read device config
+ }
};
final ProviderInfo info = sIsolatedContext.getPackageManager()
@@ -1069,6 +1111,11 @@ public class MediaProviderTest {
public int getCallingPackageTargetSdkVersion() {
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
+
+ @Override
+ protected void checkDeviceConfigAndUpdateGetContentAlias() {
+ // Ignore this as test app cannot read device config
+ }
};
final ProviderInfo info = sIsolatedContext.getPackageManager()
.resolveContentProvider(MediaStore.AUTHORITY, PackageManager.GET_META_DATA);
diff --git a/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java b/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
index 2f90d2fa9..adeaa3b33 100644
--- a/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
+++ b/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
@@ -23,6 +23,7 @@ import static android.provider.CloudMediaProviderContract.EXTRA_SYNC_GENERATION;
import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
import static android.provider.CloudMediaProviderContract.MediaColumns;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.LONG_DEFAULT;
+import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.STRING_ARRAY_DEFAULT;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.STRING_DEFAULT;
import android.content.ContentResolver;
@@ -34,7 +35,6 @@ import android.os.SystemClock;
import android.provider.CloudMediaProvider;
import android.provider.CloudMediaProviderContract;
import android.text.TextUtils;
-import android.util.Log;
import com.android.providers.media.photopicker.LocalProvider;
@@ -96,8 +96,9 @@ public class PickerProviderMediaGenerator {
private Bundle mCursorExtra;
// TODO(b/214592293): Add pagination support for testing purposes.
- public Cursor getMedia(long generation, String albumId, String mimeType, long sizeBytes) {
- final Cursor cursor = getCursor(mMedia, generation, albumId, mimeType, sizeBytes,
+ public Cursor getMedia(long generation, String albumId, String[] mimeTypes,
+ long sizeBytes) {
+ final Cursor cursor = getCursor(mMedia, generation, albumId, mimeTypes, sizeBytes,
/* isDeleted */ false);
if (mCursorExtra != null) {
@@ -112,8 +113,8 @@ public class PickerProviderMediaGenerator {
return cursor;
}
- public Cursor getAlbums(String mimeType, long sizeBytes, boolean isLocal) {
- final Cursor cursor = getCursor(mAlbums, mimeType, sizeBytes, isLocal);
+ public Cursor getAlbums(String[] mimeTypes, long sizeBytes, boolean isLocal) {
+ final Cursor cursor = getCursor(mAlbums, mimeTypes, sizeBytes, isLocal);
if (mCursorExtra != null) {
cursor.setExtras(mCursorExtra);
@@ -130,7 +131,7 @@ public class PickerProviderMediaGenerator {
// TODO(b/214592293): Add pagination support for testing purposes.
public Cursor getDeletedMedia(long generation) {
final Cursor cursor = getCursor(mDeletedMedia, generation, /* albumId */ STRING_DEFAULT,
- /* mimeType */ STRING_DEFAULT, /* sizeBytes */ LONG_DEFAULT,
+ STRING_ARRAY_DEFAULT, /* sizeBytes */ LONG_DEFAULT,
/* isDeleted */ true);
if (mCursorExtra != null) {
@@ -260,7 +261,7 @@ public class PickerProviderMediaGenerator {
}
private static Cursor getCursor(List<TestMedia> mediaList, long generation,
- String albumId, String mimeType, long sizeBytes, boolean isDeleted) {
+ String albumId, String[] mimeTypes, long sizeBytes, boolean isDeleted) {
final MatrixCursor matrix;
if (isDeleted) {
matrix = new MatrixCursor(DELETED_MEDIA_PROJECTION);
@@ -272,22 +273,22 @@ public class PickerProviderMediaGenerator {
for (TestMedia media : mediaList) {
if (!TextUtils.isEmpty(albumId) && matchesFilter(media,
- albumId, mimeType, sizeBytes)) {
+ albumId, mimeTypes, sizeBytes)) {
matrix.addRow(media.toAlbumMediaArray());
} else if (media.generation > generation
- && matchesFilter(media, albumId, mimeType, sizeBytes)) {
+ && matchesFilter(media, albumId, mimeTypes, sizeBytes)) {
matrix.addRow(media.toArray(isDeleted));
}
}
return matrix;
}
- private static Cursor getCursor(List<TestAlbum> albumList, String mimeType, long sizeBytes,
- boolean isLocal) {
+ private static Cursor getCursor(List<TestAlbum> albumList, String[] mimeTypes,
+ long sizeBytes, boolean isLocal) {
final MatrixCursor matrix = new MatrixCursor(ALBUM_PROJECTION);
for (TestAlbum album : albumList) {
- final String[] res = album.toArray(mimeType, sizeBytes, isLocal);
+ final String[] res = album.toArray(mimeTypes, sizeBytes, isLocal);
if (res != null) {
matrix.addRow(res);
}
@@ -398,13 +399,13 @@ public class PickerProviderMediaGenerator {
this.media = media;
}
- public String[] toArray(String mimeType, long sizeBytes, boolean isLocal) {
+ public String[] toArray(String[] mimeTypes, long sizeBytes, boolean isLocal) {
long mediaCount = 0;
String mediaCoverId = null;
long dateTakenMs = 0;
for (TestMedia m : media) {
- if (matchesFilter(m, id, mimeType, sizeBytes)) {
+ if (matchesFilter(m, id, mimeTypes, sizeBytes)) {
if (mediaCount++ == 0) {
mediaCoverId = m.getId();
dateTakenMs = m.dateTakenMs;
@@ -442,14 +443,26 @@ public class PickerProviderMediaGenerator {
}
}
- private static boolean matchesFilter(TestMedia media, String albumId, String mimeType,
+ private static boolean matchesFilter(TestMedia media, String albumId, String[] mimeTypes,
long sizeBytes) {
if (!Objects.equals(albumId, STRING_DEFAULT) && !Objects.equals(albumId, media.albumId)) {
return false;
}
- if (!Objects.equals(mimeType, STRING_DEFAULT) && !media.mimeType.startsWith(mimeType)) {
- return false;
+
+ if (mimeTypes != null) {
+ boolean matchesMimeType = false;
+ for (String m : mimeTypes) {
+ if (m != null && media.mimeType.startsWith(m)) {
+ matchesMimeType = true;
+ break;
+ }
+ }
+
+ if (!matchesMimeType) {
+ return false;
+ }
}
+
if (sizeBytes != LONG_DEFAULT && media.sizeBytes > sizeBytes) {
return false;
}
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
index a007139ad..d56328e0c 100644
--- a/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
+++ b/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
@@ -54,7 +54,7 @@ public class CloudProviderPrimary extends CloudMediaProvider {
CloudProviderQueryExtras.fromCloudMediaBundle(extras);
return mMediaGenerator.getMedia(queryExtras.getGeneration(), queryExtras.getAlbumId(),
- queryExtras.getMimeType(), queryExtras.getSizeBytes());
+ queryExtras.getMimeTypes(), queryExtras.getSizeBytes());
}
@Override
@@ -70,7 +70,7 @@ public class CloudProviderPrimary extends CloudMediaProvider {
final CloudProviderQueryExtras queryExtras =
CloudProviderQueryExtras.fromCloudMediaBundle(extras);
- return mMediaGenerator.getAlbums(queryExtras.getMimeType(), queryExtras.getSizeBytes(),
+ return mMediaGenerator.getAlbums(queryExtras.getMimeTypes(), queryExtras.getSizeBytes(),
/* isLocal */ false);
}
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java
index abcb92f9e..a00cbafc3 100644
--- a/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java
+++ b/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java
@@ -16,7 +16,6 @@
package com.android.providers.media.cloudproviders;
-import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator;
import android.content.res.AssetFileDescriptor;
@@ -54,7 +53,7 @@ public class CloudProviderSecondary extends CloudMediaProvider {
CloudProviderQueryExtras.fromCloudMediaBundle(extras);
return mMediaGenerator.getMedia(queryExtras.getGeneration(), queryExtras.getAlbumId(),
- queryExtras.getMimeType(), queryExtras.getSizeBytes());
+ queryExtras.getMimeTypes(), queryExtras.getSizeBytes());
}
@Override
@@ -70,7 +69,7 @@ public class CloudProviderSecondary extends CloudMediaProvider {
final CloudProviderQueryExtras queryExtras =
CloudProviderQueryExtras.fromCloudMediaBundle(extras);
- return mMediaGenerator.getAlbums(queryExtras.getMimeType(), queryExtras.getSizeBytes(),
+ return mMediaGenerator.getAlbums(queryExtras.getMimeTypes(), queryExtras.getSizeBytes(),
/* isLocal */ false);
}
diff --git a/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java b/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java
index 7608c928b..dbddc3eb1 100644
--- a/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java
+++ b/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java
@@ -482,7 +482,7 @@ public class ItemsProviderTest {
File videoFile = assertCreateNewVideo();
try {
final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "image/*", /* userId */ null);
+ /* limit */ -1, /* mimeType */ new String[]{ "image/*"}, /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(1);
@@ -504,7 +504,7 @@ public class ItemsProviderTest {
File imageFile = assertCreateNewImage();
try {
final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "image/png", /* userId */ null);
+ /* limit */ -1, /* mimeType */ new String[]{"image/png"}, /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(0);
} finally {
@@ -526,7 +526,7 @@ public class ItemsProviderTest {
File videoFileHidden = assertCreateNewVideo(hiddenDir);
try {
final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "image/*", /* userId */ null);
+ /* limit */ -1, /* mimeType */ new String[]{"image/*"}, /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(0);
} finally {
@@ -549,7 +549,7 @@ public class ItemsProviderTest {
File videoFile = assertCreateNewVideo();
try {
final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "video/*", /* userId */ null);
+ /* limit */ -1, /* mimeType */ new String[]{"video/*"}, /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(1);
@@ -571,7 +571,7 @@ public class ItemsProviderTest {
File videoFile = assertCreateNewVideo();
try {
final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "video/mp4", /* userId */ null);
+ /* limit */ -1, /* mimeType */ new String[]{"video/mp4"}, /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(1);
} finally {
@@ -592,7 +592,7 @@ public class ItemsProviderTest {
File videoFileHidden = assertCreateNewVideo(hiddenDir);
try {
final Cursor res = mItemsProvider.getItems(Category.DEFAULT, /* offset */ 0,
- /* limit */ -1, /* mimeType */ "video/*", /* userId */ null);
+ /* limit */ -1, /* mimeType */ new String[]{"video/*"}, /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(0);
} finally {
diff --git a/tests/src/com/android/providers/media/photopicker/LocalProvider.java b/tests/src/com/android/providers/media/photopicker/LocalProvider.java
index 3916c9daa..b1c1281a3 100644
--- a/tests/src/com/android/providers/media/photopicker/LocalProvider.java
+++ b/tests/src/com/android/providers/media/photopicker/LocalProvider.java
@@ -16,7 +16,6 @@
package com.android.providers.media.photopicker;
-import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator;
import android.content.res.AssetFileDescriptor;
@@ -53,7 +52,7 @@ public class LocalProvider extends CloudMediaProvider {
CloudProviderQueryExtras.fromCloudMediaBundle(extras);
return mMediaGenerator.getMedia(queryExtras.getGeneration(), queryExtras.getAlbumId(),
- queryExtras.getMimeType(), queryExtras.getSizeBytes());
+ queryExtras.getMimeTypes(), queryExtras.getSizeBytes());
}
@Override
@@ -69,7 +68,7 @@ public class LocalProvider extends CloudMediaProvider {
final CloudProviderQueryExtras queryExtras =
CloudProviderQueryExtras.fromCloudMediaBundle(extras);
- return mMediaGenerator.getAlbums(queryExtras.getMimeType(), queryExtras.getSizeBytes(),
+ return mMediaGenerator.getAlbums(queryExtras.getMimeTypes(), queryExtras.getSizeBytes(),
/* isLocal */ true);
}
diff --git a/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java b/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
index 6931cd67d..0f8e79d3f 100644
--- a/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
+++ b/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
@@ -559,7 +559,9 @@ public class PickerDataLayerTest {
private static Bundle buildQueryArgs(String mimeType, long sizeBytes) {
final Bundle queryArgs = new Bundle();
- queryArgs.putString(MediaStore.QUERY_ARG_MIME_TYPE, mimeType);
+ if (mimeType != null) {
+ queryArgs.putStringArray(MediaStore.QUERY_ARG_MIME_TYPE, new String[]{mimeType});
+ }
queryArgs.putLong(MediaStore.QUERY_ARG_SIZE_BYTES, sizeBytes);
return queryArgs;
diff --git a/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java b/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
index 53d6837d7..bab7246b2 100644
--- a/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
+++ b/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
@@ -1052,8 +1052,10 @@ public class PickerSyncControllerTest {
when(mockContext.getResources()).thenReturn(mockResources);
when(mockContext.getPackageManager()).thenReturn(mContext.getPackageManager());
- when(mockContext.getSystemService(StorageManager.class))
- .thenReturn(mContext.getSystemService(StorageManager.class));
+ when(mockContext.getSystemServiceName(StorageManager.class)).thenReturn(
+ mContext.getSystemServiceName(StorageManager.class));
+ when(mockContext.getSystemService(StorageManager.class)).thenReturn(
+ mContext.getSystemService(StorageManager.class));
when(mockContext.getSharedPreferences(anyString(), anyInt())).thenAnswer(i -> {
return mContext.getSharedPreferences((String)i.getArgument(0), (int)i.getArgument(1));
});
diff --git a/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java b/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java
index 3a37efaf2..3e07a2e50 100644
--- a/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java
+++ b/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java
@@ -81,7 +81,9 @@ public class ExternalDbFacadeTest {
private static final long GENERATION_MODIFIED5 = 5;
private static final long SIZE = 8000;
private static final String IMAGE_MIME_TYPE = "image/jpeg";
+ private static final String[] IMAGE_MIME_TYPES_QUERY = new String[]{"image/jpeg"};
private static final String VIDEO_MIME_TYPE = "video/mp4";
+ private static final String[] VIDEO_MIME_TYPES_QUERY = new String[]{"video/mp4"};
private static final long DURATION_MS = 5;
private static final int IS_FAVORITE = 0;
@@ -542,12 +544,12 @@ public class ExternalDbFacadeTest {
}
try (Cursor cursor = facade.queryMedia(/* generation */ 0,
- /* albumId */ null, VIDEO_MIME_TYPE)) {
+ /* albumId */ null, VIDEO_MIME_TYPES_QUERY)) {
assertThat(cursor.getCount()).isEqualTo(0);
}
try (Cursor cursor = facade.queryMedia(/* generation */ 0,
- /* albumId */ null, IMAGE_MIME_TYPE)) {
+ /* albumId */ null, IMAGE_MIME_TYPES_QUERY)) {
assertThat(cursor.getCount()).isEqualTo(1);
cursor.moveToFirst();
@@ -616,17 +618,17 @@ public class ExternalDbFacadeTest {
}
try (Cursor cursor = facade.queryMedia(/* generation */ 0,
- ALBUM_ID_SCREENSHOTS, IMAGE_MIME_TYPE)) {
+ ALBUM_ID_SCREENSHOTS, IMAGE_MIME_TYPES_QUERY)) {
assertThat(cursor.getCount()).isEqualTo(0);
}
try (Cursor cursor = facade.queryMedia(/* generation */ 0,
- ALBUM_ID_CAMERA, VIDEO_MIME_TYPE)) {
+ ALBUM_ID_CAMERA, VIDEO_MIME_TYPES_QUERY)) {
assertThat(cursor.getCount()).isEqualTo(0);
}
try (Cursor cursor = facade.queryMedia(/* generation */ 0,
- ALBUM_ID_CAMERA, IMAGE_MIME_TYPE)) {
+ ALBUM_ID_CAMERA, IMAGE_MIME_TYPES_QUERY)) {
assertThat(cursor.getCount()).isEqualTo(1);
cursor.moveToFirst();
@@ -762,7 +764,7 @@ public class ExternalDbFacadeTest {
cv1.put(MediaColumns.RELATIVE_PATH, ExternalDbFacade.RELATIVE_PATH_CAMERA);
helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv1));
- // Insert video in camera ablum
+ // Insert video in camera album
ContentValues cv2 = getContentValues(DATE_TAKEN_MS5, GENERATION_MODIFIED5);
cv2.put(FileColumns.MIME_TYPE, VIDEO_MIME_TYPE);
cv2.put(FileColumns.MEDIA_TYPE, FileColumns.MEDIA_TYPE_VIDEO);
@@ -772,7 +774,7 @@ public class ExternalDbFacadeTest {
assertThat(cursor.getCount()).isEqualTo(2);
}
- try (Cursor cursor = facade.queryAlbums(IMAGE_MIME_TYPE)) {
+ try (Cursor cursor = facade.queryAlbums(IMAGE_MIME_TYPES_QUERY)) {
assertThat(cursor.getCount()).isEqualTo(1);
// We verify the order of the albums only the image in camera is shown
diff --git a/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java b/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
index 264e9bd16..5e13bec9c 100644
--- a/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
+++ b/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
@@ -49,7 +49,9 @@ public class PickerDbFacadeTest {
private static final String CLOUD_ID = "asdfghjkl;";
private static final String ALBUM_ID = "testAlbum";
private static final String VIDEO_MIME_TYPE = "video/mp4";
+ private static final String[] VIDEO_MIME_TYPES_QUERY = new String[]{"video/mp4"};
private static final String IMAGE_MIME_TYPE = "image/jpeg";
+ private static final String[] IMAGE_MIME_TYPES_QUERY = new String[]{"image/jpeg"};
private static final int STANDARD_MIME_TYPE_EXTENSION =
MediaColumns.STANDARD_MIME_TYPE_EXTENSION_GIF;
@@ -609,7 +611,7 @@ public class PickerDbFacadeTest {
}
@Test
- public void testQueryWithMimeTypeFilter() throws Exception {
+ public void testQueryWithMimeTypesFilter() throws Exception {
Cursor cursor1 = getMediaCursor(LOCAL_ID, DATE_TAKEN_MS, GENERATION_MODIFIED,
/* mediaStoreUri */ null, SIZE_BYTES, "video/webm",
STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
@@ -622,12 +624,12 @@ public class PickerDbFacadeTest {
// Verify all
PickerDbFacade.QueryFilterBuilder qfbAll = new PickerDbFacade.QueryFilterBuilder(1000);
- qfbAll.setMimeType("*/*");
+ qfbAll.setMimeTypes(new String[]{"*/*"});
try (Cursor cr = mFacade.queryMediaForUi(qfbAll.build())) {
assertThat(cr.getCount()).isEqualTo(2);
}
- qfbAll.setMimeType("video/mp4");
+ qfbAll.setMimeTypes(new String[]{"video/mp4"});
try (Cursor cr = mFacade.queryMediaForUi(qfbAll.build())) {
assertThat(cr.getCount()).isEqualTo(1);
@@ -639,14 +641,14 @@ public class PickerDbFacadeTest {
PickerDbFacade.QueryFilterBuilder qfbAfter = new PickerDbFacade.QueryFilterBuilder(1000);
qfbAfter.setDateTakenAfterMs(DATE_TAKEN_MS - 1);
qfbAfter.setId(0);
- qfbAfter.setMimeType("video/*");
+ qfbAfter.setMimeTypes(new String[]{"video/*"});
try (Cursor cr = mFacade.queryMediaForUi(qfbAfter.build())) {
assertThat(cr.getCount()).isEqualTo(2);
}
qfbAfter.setDateTakenAfterMs(DATE_TAKEN_MS - 1);
qfbAfter.setId(0);
- qfbAfter.setMimeType("video/webm");
+ qfbAfter.setMimeTypes(new String[]{"video/webm"});
try (Cursor cr = mFacade.queryMediaForUi(qfbAfter.build())) {
assertThat(cr.getCount()).isEqualTo(1);
@@ -658,14 +660,14 @@ public class PickerDbFacadeTest {
PickerDbFacade.QueryFilterBuilder qfbBefore = new PickerDbFacade.QueryFilterBuilder(1000);
qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS + 1);
qfbBefore.setId(0);
- qfbBefore.setMimeType("video/*");
+ qfbBefore.setMimeTypes(new String[]{"video/*"});
try (Cursor cr = mFacade.queryMediaForUi(qfbBefore.build())) {
assertThat(cr.getCount()).isEqualTo(2);
}
qfbBefore.setDateTakenBeforeMs(DATE_TAKEN_MS + 1);
qfbBefore.setId(0);
- qfbBefore.setMimeType("video/mp4");
+ qfbBefore.setMimeTypes(new String[]{"video/mp4"});
try (Cursor cr = mFacade.queryMediaForUi(qfbBefore.build())) {
assertThat(cr.getCount()).isEqualTo(1);
@@ -675,7 +677,7 @@ public class PickerDbFacadeTest {
}
@Test
- public void testQueryWithSizeAndMimeTypeFilter() throws Exception {
+ public void testQueryWithSizeAndMimeTypesFilter() throws Exception {
Cursor cursor1 = getMediaCursor(LOCAL_ID, DATE_TAKEN_MS, GENERATION_MODIFIED,
/* mediaStoreUri */ null, /* sizeBytes */ 2, "video/webm",
STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ false);
@@ -688,14 +690,14 @@ public class PickerDbFacadeTest {
// mime_type and size filter matches all
PickerDbFacade.QueryFilterBuilder qfbAll = new PickerDbFacade.QueryFilterBuilder(1000);
- qfbAll.setMimeType("*/*");
+ qfbAll.setMimeTypes(new String[]{"*/*"});
qfbAll.setSizeBytes(10);
try (Cursor cr = mFacade.queryMediaForUi(qfbAll.build())) {
assertThat(cr.getCount()).isEqualTo(2);
}
// mime_type and size filter matches none
- qfbAll.setMimeType("video/webm");
+ qfbAll.setMimeTypes(new String[]{"video/webm"});
qfbAll.setSizeBytes(1);
try (Cursor cr = mFacade.queryMediaForUi(qfbAll.build())) {
assertThat(cr.getCount()).isEqualTo(0);
@@ -882,7 +884,7 @@ public class PickerDbFacadeTest {
}
@Test
- public void testGetFavoritesAlbumWithMimeTypeFilter() throws Exception {
+ public void testGetFavoritesAlbumWithMimeTypesFilter() throws Exception {
Cursor localCursor1 = getMediaCursor(LOCAL_ID + "1", DATE_TAKEN_MS, GENERATION_MODIFIED,
/* mediaStoreUri */ null, SIZE_BYTES, VIDEO_MIME_TYPE,
STANDARD_MIME_TYPE_EXTENSION, /* isFavorite */ true);
@@ -933,7 +935,7 @@ public class PickerDbFacadeTest {
/* count */ 2);
}
- qfb.setMimeType(IMAGE_MIME_TYPE);
+ qfb.setMimeTypes(IMAGE_MIME_TYPES_QUERY);
try (Cursor cr = mFacade.getMergedAlbums(qfb.build())) {
assertThat(cr.getCount()).isEqualTo(1);
cr.moveToFirst();
@@ -945,7 +947,7 @@ public class PickerDbFacadeTest {
/* count */ 1);
}
- qfb.setMimeType(VIDEO_MIME_TYPE);
+ qfb.setMimeTypes(VIDEO_MIME_TYPES_QUERY);
try (Cursor cr = mFacade.getMergedAlbums(qfb.build())) {
assertThat(cr.getCount()).isEqualTo(2);
cr.moveToFirst();
@@ -964,7 +966,7 @@ public class PickerDbFacadeTest {
/* count */ 2);
}
- qfb.setMimeType("foo");
+ qfb.setMimeTypes(new String[]{"foo"});
try (Cursor cr = mFacade.getMergedAlbums(qfb.build())) {
assertThat(cr.getCount()).isEqualTo(0);
}
@@ -1113,7 +1115,8 @@ public class PickerDbFacadeTest {
}
private static Cursor getAlbumMediaCursor(String id, long dateTakenMs, long generationModified,
- String mediaStoreUri, long sizeBytes, String mimeType, int standardMimeTypeExtension) {
+ String mediaStoreUri, long sizeBytes, String mimeType,
+ int standardMimeTypeExtension) {
String[] projectionKey = new String[] {
MediaColumns.ID,
MediaColumns.MEDIA_STORE_URI,
diff --git a/tests/src/com/android/providers/media/photopicker/util/MimeFilterUtilsTest.java b/tests/src/com/android/providers/media/photopicker/util/MimeFilterUtilsTest.java
index 2c98c3f14..966f0ed85 100644
--- a/tests/src/com/android/providers/media/photopicker/util/MimeFilterUtilsTest.java
+++ b/tests/src/com/android/providers/media/photopicker/util/MimeFilterUtilsTest.java
@@ -18,10 +18,15 @@ package com.android.providers.media.photopicker.util;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
import android.content.Intent;
+import android.provider.MediaStore;
import org.junit.Test;
+import java.util.Arrays;
+
public class MimeFilterUtilsTest {
private static final String[] MEDIA_MIME_TYPES = new String[] {"image/*", "video/*"};
@@ -59,7 +64,7 @@ public class MimeFilterUtilsTest {
intent.setType(mimeType);
intent.putExtra(Intent.EXTRA_MIME_TYPES, MEDIA_MIME_TYPES);
- assertThat(MimeFilterUtils.requiresUnsupportedFilters(intent)).isTrue();
+ assertThat(MimeFilterUtils.requiresUnsupportedFilters(intent)).isFalse();
}
@Test
@@ -80,7 +85,7 @@ public class MimeFilterUtilsTest {
intent.setType(mimeType);
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {"video/mp4", "image/gif"});
- assertThat(MimeFilterUtils.requiresUnsupportedFilters(intent)).isTrue();
+ assertThat(MimeFilterUtils.requiresUnsupportedFilters(intent)).isFalse();
}
@Test
@@ -102,25 +107,47 @@ public class MimeFilterUtilsTest {
}
@Test
- public void testGetMimeTypeFilter() {
+ public void testGetMimeTypeFilter_setType() {
Intent intent = new Intent();
String mimeType = "image/*";
intent.setType(mimeType);
- assertThat(MimeFilterUtils.getMimeTypeFilter(intent).equals(mimeType)).isTrue();
+ assertThat(Arrays.equals(MimeFilterUtils.getMimeTypeFilters(intent),
+ new String[]{mimeType})).isTrue();
mimeType = "video/mp4";
intent.setType(mimeType);
- assertThat(MimeFilterUtils.getMimeTypeFilter(intent).equals(mimeType)).isTrue();
mimeType = "*/*";
intent.setType(mimeType);
- assertThat(MimeFilterUtils.getMimeTypeFilter(intent)).isNull();
+ assertThat(MimeFilterUtils.getMimeTypeFilters(intent)).isNull();
+ // Test EXTRA_MIME_TYPE has higher priority than setType
mimeType = "image/*";
intent.setType(mimeType);
- String[] extraMimeTypes = new String[] {"video/*"};
+ String[] extraMimeTypes = new String[] {"video/mp4", "video/dng"};
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeTypes);
+ assertThat(Arrays.equals(MimeFilterUtils.getMimeTypeFilters(intent),
+ extraMimeTypes)).isTrue();
+ }
+
+ @Test
+ public void testGetMimeTypeFilter_extraMimeType_pickImages() {
+ Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ String[] extraMimeTypes = new String[] {"audio/mp4", "video/dng"};
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeTypes);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ MimeFilterUtils.getMimeTypeFilters(intent);
+ });
+ }
+
+ @Test
+ public void testGetMimeTypeFilter_extraMimeType_getContent() {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ String[] extraMimeTypes = new String[] {"audio/mp4", "video/dng"};
intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeTypes);
- assertThat(MimeFilterUtils.getMimeTypeFilter(intent).equals("video/*")).isTrue();
+
+ assertThat(MimeFilterUtils.getMimeTypeFilters(intent)).isNull();
}
}
diff --git a/tests/src/com/android/providers/media/photopicker/viewmodel/PickerViewModelTest.java b/tests/src/com/android/providers/media/photopicker/viewmodel/PickerViewModelTest.java
index ec4afa191..ab5069639 100644
--- a/tests/src/com/android/providers/media/photopicker/viewmodel/PickerViewModelTest.java
+++ b/tests/src/com/android/providers/media/photopicker/viewmodel/PickerViewModelTest.java
@@ -21,6 +21,7 @@ import static android.provider.CloudMediaProviderContract.MediaColumns;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -30,7 +31,7 @@ import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
-import android.provider.CloudMediaProviderContract;
+import android.provider.MediaStore;
import android.text.format.DateUtils;
import androidx.annotation.NonNull;
@@ -325,7 +326,7 @@ public class PickerViewModelTest {
@Override
public Cursor getItems(Category category, int offset,
- int limit, @Nullable String mimeType, @Nullable UserId userId) throws
+ int limit, @Nullable String[] mimeType, @Nullable UserId userId) throws
IllegalArgumentException, IllegalStateException {
final MatrixCursor c = new MatrixCursor(MediaColumns.ALL_PROJECTION);
@@ -349,7 +350,7 @@ public class PickerViewModelTest {
}
@Nullable
- public Cursor getCategories(@Nullable String mimeType, @Nullable UserId userId) {
+ public Cursor getCategories(@Nullable String[] mimeType, @Nullable UserId userId) {
if (mCategoriesCursor != null) {
return mCategoriesCursor;
}
@@ -368,31 +369,75 @@ public class PickerViewModelTest {
}
@Test
- public void testParseValuesFromIntent_noMimeType_defaultFalse() {
- final Intent intent = new Intent();
+ public void testParseValuesFromPickImagesIntent_noMimeType_defaultFalse() {
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
mPickerViewModel.parseValuesFromIntent(intent);
- assertThat(mPickerViewModel.hasMimeTypeFilter()).isFalse();
+ assertThat(mPickerViewModel.hasMimeTypeFilters()).isFalse();
+ }
+
+ @Test
+ public void testParseValuesFromGetContentIntent_noMimeType_defaultFalse() {
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ intent.setType("*/*");
+
+ mPickerViewModel.parseValuesFromIntent(intent);
+
+ assertThat(mPickerViewModel.hasMimeTypeFilters()).isFalse();
}
@Test
public void testParseValuesFromIntent_validMimeType() {
- final Intent intent = new Intent();
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
intent.setType("image/png");
mPickerViewModel.parseValuesFromIntent(intent);
- assertThat(mPickerViewModel.hasMimeTypeFilter()).isTrue();
+ assertThat(mPickerViewModel.hasMimeTypeFilters()).isTrue();
+ }
+
+ @Test
+ public void testParseValuesFromPickImagesIntent_validExtraMimeType() {
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {"image/gif", "video/*"});
+
+ mPickerViewModel.parseValuesFromIntent(intent);
+
+ assertThat(mPickerViewModel.hasMimeTypeFilters()).isTrue();
+ }
+
+ @Test
+ public void testParseValuesFromPickImagesIntent_invalidExtraMimeType() {
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {"audio/*", "video/*"});
+
+ try {
+ mPickerViewModel.parseValuesFromIntent(intent);
+ fail("Photo Picker does not support non-media mime type filters");
+ } catch (IllegalArgumentException expected) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void testParseValuesFromGetContentIntent_validExtraMimeType() {
+ final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {"image/gif", "video/*"});
+
+ mPickerViewModel.parseValuesFromIntent(intent);
+
+ assertThat(mPickerViewModel.hasMimeTypeFilters()).isTrue();
}
@Test
- public void testParseValuesFromIntent_ignoreInvalidMimeType() {
- final Intent intent = new Intent();
- intent.setType("audio/*");
+ public void testParseValuesFromGetContentIntent_invalidExtraMimeType() {
+ final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {"audio/*", "video/*"});
mPickerViewModel.parseValuesFromIntent(intent);
- assertThat(mPickerViewModel.hasMimeTypeFilter()).isFalse();
+ // non-media filters for GET_CONTENT show all images and videos
+ assertThat(mPickerViewModel.hasMimeTypeFilters()).isFalse();
}
}
diff --git a/tests/src/com/android/providers/media/scan/MediaScannerTest.java b/tests/src/com/android/providers/media/scan/MediaScannerTest.java
index f8c27e6a6..4e0e26592 100644
--- a/tests/src/com/android/providers/media/scan/MediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/MediaScannerTest.java
@@ -130,6 +130,11 @@ public class MediaScannerTest {
protected void updateNextRowIdXattr(DatabaseHelper helper, long id) {
// Ignoring this as test app would not have access to update xattr.
}
+
+ @Override
+ protected void checkDeviceConfigAndUpdateGetContentAlias() {
+ // Ignore this as test app cannot read device config
+ }
};
mProvider.attachInfo(this, info);
mResolver.addProvider(MediaStore.AUTHORITY, mProvider);
diff --git a/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java b/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
index 0450c1cee..e3c56f16e 100644
--- a/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
+++ b/tests/src/com/android/providers/media/scan/ModernMediaScannerTest.java
@@ -198,8 +198,14 @@ public class ModernMediaScannerTest {
assertEquals(270,
(int) parseOptionalOrientation(ExifInterface.ORIENTATION_ROTATE_270).get());
- // We can't represent this as an orientation
- assertFalse(parseOptionalOrientation(ExifInterface.ORIENTATION_TRANSPOSE).isPresent());
+ assertEquals(0,
+ (int) parseOptionalOrientation(ExifInterface.ORIENTATION_FLIP_HORIZONTAL).get());
+ assertEquals(90,
+ (int) parseOptionalOrientation(ExifInterface.ORIENTATION_TRANSPOSE).get());
+ assertEquals(180,
+ (int) parseOptionalOrientation(ExifInterface.ORIENTATION_FLIP_VERTICAL).get());
+ assertEquals(270,
+ (int) parseOptionalOrientation(ExifInterface.ORIENTATION_TRANSVERSE).get());
}
@Test
diff --git a/tests/src/com/android/providers/media/util/FileUtilsTest.java b/tests/src/com/android/providers/media/util/FileUtilsTest.java
index 266c14473..a1f2b8237 100644
--- a/tests/src/com/android/providers/media/util/FileUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/FileUtilsTest.java
@@ -63,6 +63,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -1021,11 +1022,16 @@ public class FileUtilsTest {
assertThat(isDataOrObbPath("/storage/emulated/0/Android/obb")).isTrue();
assertThat(isDataOrObbPath("/storage/ABCD-1234/Android/data")).isTrue();
assertThat(isDataOrObbPath("/storage/ABCD-1234/Android/obb")).isTrue();
- assertThat(isDataOrObbPath("/storage/emulated/0/Android/data/foo")).isTrue();
- assertThat(isDataOrObbPath("/storage/emulated/0/Android/obb/foo")).isTrue();
- assertThat(isDataOrObbPath("/storage/ABCD-1234/Android/data/foo")).isTrue();
- assertThat(isDataOrObbPath("/storage/ABCD-1234/Android/obb/foo")).isTrue();
+ assertThat(isDataOrObbPath("/storage/emulated/0/Android/data/foo")).isFalse();
+ assertThat(isDataOrObbPath("/storage/emulated/0/Android/obb/foo")).isFalse();
+ assertThat(isDataOrObbPath("/storage/ABCD-1234/Android/data/foo")).isFalse();
+ assertThat(isDataOrObbPath("/storage/ABCD-1234/Android/obb/foo")).isFalse();
+ assertThat(isDataOrObbPath("/storage/emulated/10/Android/obb/foo")).isFalse();
+ assertThat(isDataOrObbPath("/storage/emulated//Android/obb/foo")).isFalse();
+ assertThat(isDataOrObbPath("/storage/emulated//Android/obb")).isFalse();
+ assertThat(isDataOrObbPath("/storage/emulated/0//Android/obb")).isFalse();
+ assertThat(isDataOrObbPath("/storage/emulated/0//Android/obb/foo")).isFalse();
assertThat(isDataOrObbPath("/storage/emulated/0/Android/")).isFalse();
assertThat(isDataOrObbPath("/storage/emulated/0/Android/media/")).isFalse();
assertThat(isDataOrObbPath("/storage/ABCD-1234/Android/media/")).isFalse();
@@ -1200,4 +1206,27 @@ public class FileUtilsTest {
assertTrue(values.containsKey(MediaColumns.BUCKET_DISPLAY_NAME));
assertNull(values.get(MediaColumns.BUCKET_DISPLAY_NAME));
}
+
+ @Test
+ public void testComputeDataFromValuesForValidPath_success() {
+ final ContentValues values = new ContentValues();
+ values.put(MediaColumns.RELATIVE_PATH, "Android/media/com.example");
+ values.put(MediaColumns.DISPLAY_NAME, "./../../abc.txt");
+
+ FileUtils.computeDataFromValues(values, new File("/storage/emulated/0"), false);
+
+ assertThat(values.getAsString(MediaColumns.DATA)).isEqualTo(
+ "/storage/emulated/0/Android/abc.txt");
+ }
+
+ @Test
+ public void testComputeDataFromValuesForInvalidPath_throwsIllegalArgumentException() {
+ final ContentValues values = new ContentValues();
+ values.put(MediaColumns.RELATIVE_PATH, "\0");
+ values.put(MediaColumns.DISPLAY_NAME, "./../../abc.txt");
+
+ assertThrows(IllegalArgumentException.class,
+ () -> FileUtils.computeDataFromValues(values, new File("/storage/emulated/0"),
+ false));
+ }
}